jtx: json, memo funclets, fixes, improvements:

* Add json funclet
* Add memo funclet
* Add Env::trace
* Add PrettAmount regression test
* Fix PrettyAmount construction
* Make Env::trust balance-neutral
* Log parse errors during signing
This commit is contained in:
Vinnie Falco
2015-06-18 12:47:39 -07:00
parent 0062a260b9
commit cd5e36045c
12 changed files with 466 additions and 5 deletions

View File

@@ -3375,6 +3375,14 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\json.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\memo.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\multisign.cpp"> <ClCompile Include="..\..\src\ripple\test\jtx\impl\multisign.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -3431,8 +3439,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\test\jtx\json.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\JTx.h"> <ClInclude Include="..\..\src\ripple\test\jtx\JTx.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\memo.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\multisign.h"> <ClInclude Include="..\..\src\ripple\test\jtx\multisign.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\noop.h"> <ClInclude Include="..\..\src\ripple\test\jtx\noop.h">

View File

@@ -4104,6 +4104,12 @@
<ClCompile Include="..\..\src\ripple\test\jtx\impl\flags.cpp"> <ClCompile Include="..\..\src\ripple\test\jtx\impl\flags.cpp">
<Filter>ripple\test\jtx\impl</Filter> <Filter>ripple\test\jtx\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\json.cpp">
<Filter>ripple\test\jtx\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\memo.cpp">
<Filter>ripple\test\jtx\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\jtx\impl\multisign.cpp"> <ClCompile Include="..\..\src\ripple\test\jtx\impl\multisign.cpp">
<Filter>ripple\test\jtx\impl</Filter> <Filter>ripple\test\jtx\impl</Filter>
</ClCompile> </ClCompile>
@@ -4146,9 +4152,15 @@
<ClCompile Include="..\..\src\ripple\test\jtx\impl\utility.cpp"> <ClCompile Include="..\..\src\ripple\test\jtx\impl\utility.cpp">
<Filter>ripple\test\jtx\impl</Filter> <Filter>ripple\test\jtx\impl</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\test\jtx\json.h">
<Filter>ripple\test\jtx</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\JTx.h"> <ClInclude Include="..\..\src\ripple\test\jtx\JTx.h">
<Filter>ripple\test\jtx</Filter> <Filter>ripple\test\jtx</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\memo.h">
<Filter>ripple\test\jtx</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\multisign.h"> <ClInclude Include="..\..\src\ripple\test\jtx\multisign.h">
<Filter>ripple\test\jtx</Filter> <Filter>ripple\test\jtx</Filter>
</ClInclude> </ClInclude>

View File

@@ -28,7 +28,9 @@
#include <ripple/test/jtx/Env.h> #include <ripple/test/jtx/Env.h>
#include <ripple/test/jtx/fee.h> #include <ripple/test/jtx/fee.h>
#include <ripple/test/jtx/flags.h> #include <ripple/test/jtx/flags.h>
#include <ripple/test/jtx/json.h>
#include <ripple/test/jtx/JTx.h> #include <ripple/test/jtx/JTx.h>
#include <ripple/test/jtx/memo.h>
#include <ripple/test/jtx/multisign.h> #include <ripple/test/jtx/multisign.h>
#include <ripple/test/jtx/noop.h> #include <ripple/test/jtx/noop.h>
#include <ripple/test/jtx/offer.h> #include <ripple/test/jtx/offer.h>

View File

@@ -126,6 +126,22 @@ public:
public: public:
Env (beast::unit_test::suite& test_); Env (beast::unit_test::suite& test_);
/** Turn on JSON tracing.
With no arguments, trace all
*/
void
trace(int howMany = -1)
{
trace_ = howMany;
}
/** Turn off JSON tracing. */
void
notrace()
{
trace_ = 0;
}
/** Associate AccountID with account. */ /** Associate AccountID with account. */
void void
memoize (Account const& account); memoize (Account const& account);
@@ -310,6 +326,17 @@ public:
These convenience functions are for easy set-up These convenience functions are for easy set-up
of the environment, they bypass fee, seq, and sig of the environment, they bypass fee, seq, and sig
settings. settings.
Preconditions:
The account must already exist
Effects:
A trust line is added for the account.
The account's sequence number is incremented.
The account is refunded for the transaction fee
to set the trust line.
The refund comes from the master account.
*/ */
/** @{ */ /** @{ */
void void
@@ -327,6 +354,8 @@ public:
/** @} */ /** @} */
protected: protected:
int trace_ = 0;
void void
autofill_sig (JTx& jt); autofill_sig (JTx& jt);

View File

@@ -167,8 +167,10 @@ struct XRP_t
PrettyAmount PrettyAmount
operator()(T v) const operator()(T v) const
{ {
return { v * return { std::conditional_t<
dropsPerXRP<T>::value }; std::is_signed<T>::value,
std::int64_t, std::uint64_t>{v} *
dropsPerXRP<T>::value };
} }
PrettyAmount PrettyAmount

View File

@@ -175,10 +175,17 @@ void
Env::trust (STAmount const& amount, Env::trust (STAmount const& amount,
Account const& account) Account const& account)
{ {
auto const start = balance(account);
apply(jtx::trust(account, amount), apply(jtx::trust(account, amount),
jtx::seq(jtx::autofill), jtx::seq(jtx::autofill),
fee(jtx::autofill), fee(jtx::autofill),
sig(jtx::autofill)); sig(jtx::autofill));
apply(pay(master, account,
drops(ledger->getBaseFee())),
jtx::seq(jtx::autofill),
fee(jtx::autofill),
sig(jtx::autofill));
test.expect(balance(account) == start);
} }
void void
@@ -211,6 +218,12 @@ Env::submit (JTx const& jt)
// we didn't get the expected result. // we didn't get the expected result.
return; return;
} }
if (trace_)
{
if (trace_ > 0)
--trace_;
test.log << pretty(jt.jv);
}
for (auto const& f : jt.requires) for (auto const& f : jt.requires)
f(*this); f(*this);
} }
@@ -243,7 +256,17 @@ Env::autofill (JTx& jt)
if(jt.fill_seq) if(jt.fill_seq)
jtx::fill_seq(jv, *ledger); jtx::fill_seq(jv, *ledger);
// Must come last // Must come last
autofill_sig(jt); try
{
autofill_sig(jt);
}
catch (parse_error const&)
{
test.log <<
"parse failed:\n" <<
pretty(jv);
throw;
}
} }
std::shared_ptr<STTx> std::shared_ptr<STTx>

View File

@@ -110,6 +110,7 @@ public:
expect(to_string(XRP(.80)) == "0.8 XRP"); expect(to_string(XRP(.80)) == "0.8 XRP");
expect(to_string(XRP(.005)) == "5000 drops"); expect(to_string(XRP(.005)) == "5000 drops");
expect(to_string(XRP(0.1)) == "0.1 XRP"); expect(to_string(XRP(0.1)) == "0.1 XRP");
expect(to_string(XRP(10000)) == "10000 XRP");
expect(to_string(drops(10)) == "10 drops"); expect(to_string(drops(10)) == "10 drops");
expect(to_string(drops(123400000)) == "123.4 XRP"); expect(to_string(drops(123400000)) == "123.4 XRP");
expect(to_string(XRP(-5)) == "-5 XRP"); expect(to_string(XRP(-5)) == "-5 XRP");
@@ -226,7 +227,7 @@ public:
env.fund(XRP(10000), "alice", gw); env.fund(XRP(10000), "alice", gw);
env.require(balance("alice", USD(none))); env.require(balance("alice", USD(none)));
env.trust(USD(100), "alice"); env.trust(USD(100), "alice");
env.require(balance("alice", XRP(10000) - drops(10))); env.require(balance("alice", XRP(10000))); // fee refunded
env.require(balance("alice", USD(0))); env.require(balance("alice", USD(0)));
env(pay(gw, "alice", USD(10)), require(balance("alice", USD(10)))); env(pay(gw, "alice", USD(10)), require(balance("alice", USD(10))));
@@ -413,6 +414,22 @@ public:
Env env(*this); Env env(*this);
} }
void
testMemo()
{
using namespace jtx;
Env env(*this);
env.fund(XRP(10000), "alice");
env(noop("alice"), memodata("data"));
env(noop("alice"), memoformat("format"));
env(noop("alice"), memotype("type"));
env(noop("alice"), memondata("format", "type"));
env(noop("alice"), memonformat("data", "type"));
env(noop("alice"), memontype("data", "format"));
env(noop("alice"), memo("data", "format", "type"));
env(noop("alice"), memo("data1", "format1", "type1"), memo("data2", "format2", "type2"));
}
void void
run() run()
{ {
@@ -430,6 +447,7 @@ public:
testMultiSign(); testMultiSign();
testMultiSign2(); testMultiSign2();
testTicket(); testTicket();
testMemo();
} }
}; };

View File

@@ -0,0 +1,47 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/test/jtx/json.h>
#include <ripple/test/jtx/utility.h>
#include <ripple/json/json_reader.h>
namespace ripple {
namespace test {
namespace jtx {
json::json(std::string const& s)
{
if (! Json::Reader().parse(s, jv_))
throw parse_error("bad json");
}
void
json::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
for (auto iter = jv_.begin();
iter != jv_.end(); ++iter)
jv[iter.key().asString()] = *iter;
}
} // jtx
} // test
} // ripple

View File

@@ -0,0 +1,107 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/test/jtx/memo.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/protocol/JsonFields.h>
namespace ripple {
namespace test {
namespace jtx {
void
memo::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoData"] = strHex(data_);
m["MemoFormat"] = strHex(format_);
m["MemoType"] = strHex(type_);
}
void
memodata::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoData"] = strHex(s_);
}
void
memoformat::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoFormat"] = strHex(s_);
}
void
memotype::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoType"] = strHex(s_);
}
void
memondata::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoFormat"] = strHex(format_);
m["MemoType"] = strHex(type_);
}
void
memonformat::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoData"] = strHex(data_);
m["MemoType"] = strHex(type_);
}
void
memontype::operator()(Env const&, JTx& jt) const
{
auto& jv = jt.jv;
auto& ma = jv["Memos"];
auto& mi = ma[ma.size()];
auto& m = mi["Memo"];
m["MemoData"] = strHex(data_);
m["MemoFormat"] = strHex(format_);
}
} // jtx
} // test
} // ripple

View File

@@ -0,0 +1,48 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_JTX_JSON_H_INCLUDED
#define RIPPLE_TEST_JTX_JSON_H_INCLUDED
#include <ripple/test/jtx/Env.h>
#include <ripple/json/json_value.h>
namespace ripple {
namespace test {
namespace jtx {
/** Inject raw JSON. */
class json
{
private:
Json::Value jv_;
public:
explicit
json (std::string const&);
void
operator()(Env const&, JTx& jt) const;
};
} // jtx
} // test
} // ripple
#endif

159
src/ripple/test/jtx/memo.h Normal file
View File

@@ -0,0 +1,159 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_JTX_MEMO_H_INCLUDED
#define RIPPLE_TEST_JTX_MEMO_H_INCLUDED
#include <ripple/test/jtx/Env.h>
#include <boost/optional.hpp>
namespace ripple {
namespace test {
namespace jtx {
/** Add a memo to a JTx.
If a memo already exists, the new
memo is appended to the array.
*/
class memo
{
private:
std::string data_;
std::string format_;
std::string type_;
public:
memo (std::string const& data,
std::string const& format,
std::string const& type)
: data_(data)
, format_(format)
, type_(type)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memodata
{
private:
std::string s_;
public:
memodata (std::string const& s)
: s_(s)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memoformat
{
private:
std::string s_;
public:
memoformat (std::string const& s)
: s_(s)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memotype
{
private:
std::string s_;
public:
memotype (std::string const& s)
: s_(s)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memondata
{
private:
std::string format_;
std::string type_;
public:
memondata (std::string const& format,
std::string const& type)
: format_(format)
, type_(type)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memonformat
{
private:
std::string data_;
std::string type_;
public:
memonformat (std::string const& data,
std::string const& type)
: data_(data)
, type_(type)
{
}
void
operator()(Env const&, JTx& jt) const;
};
class memontype
{
private:
std::string data_;
std::string format_;
public:
memontype (std::string const& data,
std::string const& format)
: data_(data)
, format_(format)
{
}
void
operator()(Env const&, JTx& jt) const;
};
} // jtx
} // test
} // ripple
#endif

View File

@@ -25,6 +25,8 @@
#include <ripple/test/jtx/impl/Env.cpp> #include <ripple/test/jtx/impl/Env.cpp>
#include <ripple/test/jtx/impl/fee.cpp> #include <ripple/test/jtx/impl/fee.cpp>
#include <ripple/test/jtx/impl/flags.cpp> #include <ripple/test/jtx/impl/flags.cpp>
#include <ripple/test/jtx/impl/json.cpp>
#include <ripple/test/jtx/impl/memo.cpp>
#include <ripple/test/jtx/impl/multisign.cpp> #include <ripple/test/jtx/impl/multisign.cpp>
#include <ripple/test/jtx/impl/offer.cpp> #include <ripple/test/jtx/impl/offer.cpp>
#include <ripple/test/jtx/impl/owners.cpp> #include <ripple/test/jtx/impl/owners.cpp>