mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-27 05:55:50 +00:00
Add check, escrow, and pay_chan to ledger_entry (RIPD-1600)
This commit is contained in:
committed by
Nikolaos D. Bougalis
parent
8eb8c77886
commit
c4a9b73a66
@@ -43,26 +43,33 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
if (!lpLedger)
|
if (!lpLedger)
|
||||||
return jvResult;
|
return jvResult;
|
||||||
|
|
||||||
uint256 uNodeIndex;
|
uint256 uNodeIndex;
|
||||||
bool bNodeBinary = false;
|
bool bNodeBinary = false;
|
||||||
|
LedgerEntryType expectedType = ltANY;
|
||||||
|
|
||||||
if (context.params.isMember (jss::index))
|
if (context.params.isMember (jss::index))
|
||||||
{
|
{
|
||||||
// XXX Needs to provide proof.
|
|
||||||
uNodeIndex.SetHex (context.params[jss::index].asString ());
|
uNodeIndex.SetHex (context.params[jss::index].asString ());
|
||||||
bNodeBinary = true;
|
bNodeBinary = true;
|
||||||
}
|
}
|
||||||
else if (context.params.isMember (jss::account_root))
|
else if (context.params.isMember (jss::account_root))
|
||||||
{
|
{
|
||||||
|
expectedType = ltACCOUNT_ROOT;
|
||||||
auto const account = parseBase58<AccountID>(
|
auto const account = parseBase58<AccountID>(
|
||||||
context.params[jss::account_root].asString());
|
context.params[jss::account_root].asString());
|
||||||
if (! account || account->isZero())
|
if (! account || account->isZero())
|
||||||
jvResult[jss::error] = "malformedAddress";
|
jvResult[jss::error] = "malformedAddress";
|
||||||
else
|
else
|
||||||
uNodeIndex = keylet::account(*account).key;
|
uNodeIndex = keylet::account(*account).key;
|
||||||
}
|
}
|
||||||
|
else if (context.params.isMember (jss::check))
|
||||||
|
{
|
||||||
|
expectedType = ltCHECK;
|
||||||
|
uNodeIndex.SetHex (context.params[jss::check].asString ());
|
||||||
|
}
|
||||||
else if (context.params.isMember (jss::directory))
|
else if (context.params.isMember (jss::directory))
|
||||||
{
|
{
|
||||||
|
expectedType = ltDIR_NODE;
|
||||||
if (context.params[jss::directory].isNull())
|
if (context.params[jss::directory].isNull())
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedRequest";
|
jvResult[jss::error] = "malformedRequest";
|
||||||
@@ -72,23 +79,31 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
uNodeIndex.SetHex (context.params[jss::directory].asString ());
|
uNodeIndex.SetHex (context.params[jss::directory].asString ());
|
||||||
}
|
}
|
||||||
else if (context.params[jss::directory].isMember (jss::sub_index)
|
else if (context.params[jss::directory].isMember (jss::sub_index)
|
||||||
&& !context.params[jss::directory][jss::sub_index].isIntegral ())
|
&& !context.params[jss::directory][jss::sub_index].isIntegral ())
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedRequest";
|
jvResult[jss::error] = "malformedRequest";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::uint64_t uSubIndex
|
std::uint64_t uSubIndex
|
||||||
= context.params[jss::directory].isMember (jss::sub_index)
|
= context.params[jss::directory].isMember (jss::sub_index)
|
||||||
? context.params[jss::directory][jss::sub_index].asUInt () : 0;
|
? context.params[jss::directory][jss::sub_index].asUInt () : 0;
|
||||||
|
|
||||||
if (context.params[jss::directory].isMember (jss::dir_root))
|
if (context.params[jss::directory].isMember (jss::dir_root))
|
||||||
{
|
{
|
||||||
uint256 uDirRoot;
|
if (context.params[jss::directory].isMember (jss::owner))
|
||||||
|
{
|
||||||
|
// May not specify both dir_root and owner.
|
||||||
|
jvResult[jss::error] = "malformedRequest";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint256 uDirRoot;
|
||||||
|
uDirRoot.SetHex (
|
||||||
|
context.params[jss::directory][jss::dir_root].asString());
|
||||||
|
|
||||||
uDirRoot.SetHex (context.params[jss::dir_root].asString ());
|
uNodeIndex = getDirNodeIndex (uDirRoot, uSubIndex);
|
||||||
|
}
|
||||||
uNodeIndex = getDirNodeIndex (uDirRoot, uSubIndex);
|
|
||||||
}
|
}
|
||||||
else if (context.params[jss::directory].isMember (jss::owner))
|
else if (context.params[jss::directory].isMember (jss::owner))
|
||||||
{
|
{
|
||||||
@@ -97,49 +112,80 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
|
|
||||||
if (! ownerID)
|
if (! ownerID)
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedAddress";
|
jvResult[jss::error] = "malformedAddress";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint256 uDirRoot = getOwnerDirIndex (*ownerID);
|
uint256 uDirRoot = getOwnerDirIndex (*ownerID);
|
||||||
uNodeIndex = getDirNodeIndex (uDirRoot, uSubIndex);
|
uNodeIndex = getDirNodeIndex (uDirRoot, uSubIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedRequest";
|
jvResult[jss::error] = "malformedRequest";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (context.params.isMember (jss::escrow))
|
||||||
|
{
|
||||||
|
expectedType = ltESCROW;
|
||||||
|
if (!context.params[jss::escrow].isObject ())
|
||||||
|
{
|
||||||
|
uNodeIndex.SetHex (context.params[jss::escrow].asString ());
|
||||||
|
}
|
||||||
|
else if (!context.params[jss::escrow].isMember (jss::owner)
|
||||||
|
|| !context.params[jss::escrow].isMember (jss::seq)
|
||||||
|
|| !context.params[jss::escrow][jss::seq].isIntegral ())
|
||||||
|
{
|
||||||
|
jvResult[jss::error] = "malformedRequest";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto const id = parseBase58<AccountID>(
|
||||||
|
context.params[jss::escrow][jss::owner].asString());
|
||||||
|
if (! id)
|
||||||
|
jvResult[jss::error] = "malformedOwner";
|
||||||
|
else
|
||||||
|
uNodeIndex = keylet::escrow (*id,
|
||||||
|
context.params[jss::escrow][jss::seq].asUInt()).key;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (context.params.isMember (jss::generator))
|
else if (context.params.isMember (jss::generator))
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "deprecatedFeature";
|
jvResult[jss::error] = "deprecatedFeature";
|
||||||
}
|
}
|
||||||
else if (context.params.isMember (jss::offer))
|
else if (context.params.isMember (jss::offer))
|
||||||
{
|
{
|
||||||
|
expectedType = ltOFFER;
|
||||||
if (!context.params[jss::offer].isObject())
|
if (!context.params[jss::offer].isObject())
|
||||||
{
|
{
|
||||||
uNodeIndex.SetHex (context.params[jss::offer].asString ());
|
uNodeIndex.SetHex (context.params[jss::offer].asString ());
|
||||||
}
|
}
|
||||||
else if (!context.params[jss::offer].isMember (jss::account)
|
else if (!context.params[jss::offer].isMember (jss::account)
|
||||||
|| !context.params[jss::offer].isMember (jss::seq)
|
|| !context.params[jss::offer].isMember (jss::seq)
|
||||||
|| !context.params[jss::offer][jss::seq].isIntegral ())
|
|| !context.params[jss::offer][jss::seq].isIntegral ())
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedRequest";
|
jvResult[jss::error] = "malformedRequest";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const id = parseBase58<AccountID>(
|
auto const id = parseBase58<AccountID>(
|
||||||
context.params[jss::offer][jss::account].asString());
|
context.params[jss::offer][jss::account].asString());
|
||||||
if (! id)
|
if (! id)
|
||||||
jvResult[jss::error] = "malformedAddress";
|
jvResult[jss::error] = "malformedAddress";
|
||||||
else
|
else
|
||||||
uNodeIndex = getOfferIndex (*id,
|
uNodeIndex = getOfferIndex (*id,
|
||||||
context.params[jss::offer][jss::seq].asUInt ());
|
context.params[jss::offer][jss::seq].asUInt ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (context.params.isMember (jss::payment_channel))
|
||||||
|
{
|
||||||
|
expectedType = ltPAYCHAN;
|
||||||
|
uNodeIndex.SetHex (context.params[jss::payment_channel].asString ());
|
||||||
|
}
|
||||||
else if (context.params.isMember (jss::ripple_state))
|
else if (context.params.isMember (jss::ripple_state))
|
||||||
{
|
{
|
||||||
|
expectedType = ltRIPPLE_STATE;
|
||||||
Currency uCurrency;
|
Currency uCurrency;
|
||||||
Json::Value jvRippleState = context.params[jss::ripple_state];
|
Json::Value jvRippleState = context.params[jss::ripple_state];
|
||||||
|
|
||||||
@@ -154,7 +200,7 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
== jvRippleState[jss::accounts][1u].asString ())
|
== jvRippleState[jss::accounts][1u].asString ())
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedRequest";
|
jvResult[jss::error] = "malformedRequest";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,23 +210,23 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
jvRippleState[jss::accounts][1u].asString());
|
jvRippleState[jss::accounts][1u].asString());
|
||||||
if (! id1 || ! id2)
|
if (! id1 || ! id2)
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedAddress";
|
jvResult[jss::error] = "malformedAddress";
|
||||||
}
|
}
|
||||||
else if (!to_currency (uCurrency,
|
else if (!to_currency (uCurrency,
|
||||||
jvRippleState[jss::currency].asString()))
|
jvRippleState[jss::currency].asString()))
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "malformedCurrency";
|
jvResult[jss::error] = "malformedCurrency";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uNodeIndex = getRippleStateIndex(
|
uNodeIndex = getRippleStateIndex(
|
||||||
*id1, *id2, uCurrency);
|
*id1, *id2, uCurrency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jvResult[jss::error] = "unknownOption";
|
jvResult[jss::error] = "unknownOption";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uNodeIndex.isNonZero ())
|
if (uNodeIndex.isNonZero ())
|
||||||
@@ -192,12 +238,15 @@ Json::Value doLedgerEntry (RPC::Context& context)
|
|||||||
if (!sleNode)
|
if (!sleNode)
|
||||||
{
|
{
|
||||||
// Not found.
|
// Not found.
|
||||||
// XXX Should also provide proof.
|
jvResult[jss::error] = "entryNotFound";
|
||||||
jvResult[jss::error] = "entryNotFound";
|
}
|
||||||
|
else if ((expectedType != ltANY) &&
|
||||||
|
(expectedType != sleNode->getType()))
|
||||||
|
{
|
||||||
|
jvResult[jss::error] = "malformedRequest";
|
||||||
}
|
}
|
||||||
else if (bNodeBinary)
|
else if (bNodeBinary)
|
||||||
{
|
{
|
||||||
// XXX Should also provide proof.
|
|
||||||
Serializer s;
|
Serializer s;
|
||||||
|
|
||||||
sleNode->add (s);
|
sleNode->add (s);
|
||||||
|
|||||||
@@ -48,6 +48,16 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(jv[jss::error_message] == msg);
|
BEAST_EXPECT(jv[jss::error_message] == msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Corrupt a valid address by replacing the 10th character with '!'.
|
||||||
|
// '!' is not part of the ripple alphabet.
|
||||||
|
std::string
|
||||||
|
makeBadAddress (std::string good)
|
||||||
|
{
|
||||||
|
std::string ret = std::move (good);
|
||||||
|
ret.replace (10, 1, 1, '!');
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void testLedgerRequest()
|
void testLedgerRequest()
|
||||||
{
|
{
|
||||||
testcase("Basic Request");
|
testcase("Basic Request");
|
||||||
@@ -152,26 +162,22 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testAccountRoot()
|
void testMissingLedgerEntryLedgerHash()
|
||||||
{
|
{
|
||||||
testcase("Basic Ledger Entry Request");
|
testcase("Missing ledger_entry ledger_hash");
|
||||||
using namespace test::jtx;
|
using namespace test::jtx;
|
||||||
Env env {*this};
|
Env env {*this};
|
||||||
Account const alice {"alice"};
|
Account const alice {"alice"};
|
||||||
env.fund(XRP(10000), alice);
|
env.fund(XRP(10000), alice);
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
auto jrr = env.rpc("ledger_closed") [jss::result];
|
|
||||||
BEAST_EXPECT(jrr[jss::ledger_hash] == to_string(env.closed()->info().hash));
|
|
||||||
BEAST_EXPECT(jrr[jss::ledger_index] == 3);
|
|
||||||
|
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
jvParams[jss::account_root] = alice.human();
|
jvParams[jss::account_root] = alice.human();
|
||||||
jvParams[jss::ledger_hash] = jrr[jss::ledger_hash];
|
jvParams[jss::ledger_hash] =
|
||||||
jrr = env.rpc ( "json", "ledger_entry", to_string(jvParams) ) [jss::result];
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
auto const jrr =
|
||||||
BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
|
env.rpc ("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||||
BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName] == XRP(10000).value().getText());
|
checkErrorValue (jrr, "lgrNotFound", "ledgerNotFound");
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLedgerFull()
|
void testLedgerFull()
|
||||||
@@ -225,71 +231,735 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 2u);
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testMalformedAccountRoot()
|
void testLedgerEntryAccountRoot()
|
||||||
{
|
{
|
||||||
testcase("Malformed Ledger Entry Request");
|
testcase ("ledger_entry Request AccountRoot");
|
||||||
using namespace test::jtx;
|
using namespace test::jtx;
|
||||||
Env env {*this};
|
Env env {*this};
|
||||||
Account const alice {"alice"};
|
Account const alice {"alice"};
|
||||||
env.fund(XRP(10000), alice);
|
env.fund (XRP(10000), alice);
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
auto jrr = env.rpc("ledger_closed") [jss::result];
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
|
||||||
Json::Value jvParams;
|
|
||||||
jvParams[jss::account_root] = std::string(alice.human()).replace(0, 2, 2, 'x');
|
|
||||||
jvParams[jss::ledger_hash] = jrr[jss::ledger_hash];
|
|
||||||
jrr = env.rpc ( "json", "ledger_entry", to_string(jvParams) ) [jss::result];
|
|
||||||
checkErrorValue(jrr, "malformedAddress", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void testNotFoundAccountRoot()
|
|
||||||
{
|
|
||||||
testcase("Ledger Entry Not Found");
|
|
||||||
using namespace test::jtx;
|
|
||||||
Env env {*this};
|
|
||||||
Account const alice {"alice"};
|
|
||||||
env.fund(XRP(10000), alice);
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
auto jrr = env.rpc("ledger_closed") [jss::result];
|
|
||||||
|
|
||||||
Json::Value jvParams;
|
|
||||||
jvParams[jss::account_root] = Account("bob").human();
|
|
||||||
jvParams[jss::ledger_hash] = jrr[jss::ledger_hash];
|
|
||||||
jrr = env.rpc ( "json", "ledger_entry", to_string(jvParams) ) [jss::result];
|
|
||||||
checkErrorValue(jrr, "entryNotFound", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void testAccountRootFromIndex()
|
|
||||||
{
|
|
||||||
testcase("Ledger Entry Request From Index");
|
|
||||||
using namespace test::jtx;
|
|
||||||
Env env {*this};
|
|
||||||
Account const alice {"alice"};
|
|
||||||
env.fund(XRP(10000), alice);
|
|
||||||
env.close();
|
|
||||||
|
|
||||||
auto jrr = env.rpc("ledger_closed") [jss::result];
|
|
||||||
BEAST_EXPECT(jrr[jss::ledger_hash] == to_string(env.closed()->info().hash));
|
|
||||||
BEAST_EXPECT(jrr[jss::ledger_index] == 3);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// Exercise ledger_closed along the way.
|
||||||
|
Json::Value const jrr = env.rpc ("ledger_closed")[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
|
||||||
|
BEAST_EXPECT(jrr[jss::ledger_index] == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
char const alicesAcctRootBinary[] {
|
||||||
|
"1100612200800000240000000225000000032D00000000554294BEBE5B569"
|
||||||
|
"A18C0A2702387C9B1E7146DC3A5850C1E87204951C6FDAA4C426240000002"
|
||||||
|
"540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string accountRootIndex;
|
||||||
|
{
|
||||||
|
// Request alice's account root.
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
jvParams[jss::account_root] = alice.human();
|
jvParams[jss::account_root] = alice.human();
|
||||||
jvParams[jss::ledger_hash] = jrr[jss::ledger_hash];
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
jrr = env.rpc ( "json", "ledger_entry", to_string(jvParams) ) [jss::result];
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||||
|
BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
|
||||||
|
accountRootIndex = jrr[jss::index].asString();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
// Request alice's account root, but with binary == true;
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
jvParams[jss::index] = jrr[jss::index];
|
jvParams[jss::account_root] = alice.human();
|
||||||
jrr = env.rpc ( "json", "ledger_entry", to_string(jvParams) ) [jss::result];
|
jvParams[jss::binary] = 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
BEAST_EXPECT(jrr.isMember(jss::node_binary));
|
BEAST_EXPECT(jrr.isMember(jss::node_binary));
|
||||||
BEAST_EXPECT(jrr[jss::node_binary] ==
|
BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
|
||||||
"1100612200800000240000000225000000032D00000000554294BEBE5B569"
|
|
||||||
"A18C0A2702387C9B1E7146DC3A5850C1E87204951C6FDAA4C426240000002"
|
|
||||||
"540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D");
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Request alice's account root using the index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::index] = accountRootIndex;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr.isMember(jss::node_binary));
|
||||||
|
BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request alice's account root by index, but with binary == false.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::index] = accountRootIndex;
|
||||||
|
jvParams[jss::binary] = 0;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||||
|
BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request using a corrupted AccountID.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::account_root] = makeBadAddress (alice.human());
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedAddress", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request an account that is not in the ledger.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::account_root] = Account("bob").human();
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "entryNotFound", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryCheck()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Check");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
env.fund (XRP(10000), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
uint256 const checkId {
|
||||||
|
getCheckIndex (env.master, env.seq (env.master))};
|
||||||
|
|
||||||
|
// Lambda to create a check.
|
||||||
|
auto checkCreate = [] (test::jtx::Account const& account,
|
||||||
|
test::jtx::Account const& dest, STAmount const& sendMax)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[sfAccount.jsonName] = account.human();
|
||||||
|
jv[sfSendMax.jsonName] = sendMax.getJson(0);
|
||||||
|
jv[sfDestination.jsonName] = dest.human();
|
||||||
|
jv[sfTransactionType.jsonName] = "CheckCreate";
|
||||||
|
jv[sfFlags.jsonName] = tfUniversal;
|
||||||
|
return jv;
|
||||||
|
};
|
||||||
|
|
||||||
|
env (checkCreate (env.master, alice, XRP(100)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
{
|
||||||
|
// Request a check.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::check] = to_string (checkId);
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfLedgerEntryType.jsonName] == "Check");
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request an index that is not a check. We'll use alice's
|
||||||
|
// account root index.
|
||||||
|
std::string accountRootIndex;
|
||||||
|
{
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::account_root] = alice.human();
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
accountRootIndex = jrr[jss::index].asString();
|
||||||
|
}
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::check] = accountRootIndex;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryDirectory()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Directory");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
Account const gw { "gateway" };
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
env.fund (XRP(10000), alice, gw);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env.trust(USD(1000), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Run up the number of directory entries so alice has two
|
||||||
|
// directory nodes.
|
||||||
|
for (int d = 1'000'032; d >= 1'000'000; --d)
|
||||||
|
{
|
||||||
|
env (offer (alice, USD (1), drops (d)));
|
||||||
|
}
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
{
|
||||||
|
// Exercise ledger_closed along the way.
|
||||||
|
Json::Value const jrr = env.rpc ("ledger_closed")[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
|
||||||
|
BEAST_EXPECT(jrr[jss::ledger_index] == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const dirRootIndex =
|
||||||
|
"A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
|
||||||
|
{
|
||||||
|
// Locate directory by index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = dirRootIndex;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Locate directory by directory root.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::dir_root] = dirRootIndex;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Locate directory by owner.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Locate directory by directory root and sub_index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::dir_root] = dirRootIndex;
|
||||||
|
jvParams[jss::directory][jss::sub_index] = 1;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Locate directory by owner and sub_index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::directory][jss::sub_index] = 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Null directory argument.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::nullValue;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Non-integer sub_index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::dir_root] = dirRootIndex;
|
||||||
|
jvParams[jss::directory][jss::sub_index] = 1.5;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed owner entry.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
|
||||||
|
std::string const badAddress = makeBadAddress (alice.human());
|
||||||
|
jvParams[jss::directory][jss::owner] = badAddress;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedAddress", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed directory object. Specify both dir_root and owner.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::directory][jss::dir_root] = dirRootIndex;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Incomplete directory object. Missing both dir_root and owner.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::directory] = Json::objectValue;
|
||||||
|
jvParams[jss::directory][jss::sub_index] = 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryEscrow()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Escrow");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
env.fund (XRP(10000), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Lambda to create an escrow.
|
||||||
|
auto escrowCreate = [] (
|
||||||
|
test::jtx::Account const& account, test::jtx::Account const& to,
|
||||||
|
STAmount const& amount, NetClock::time_point const& cancelAfter)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "EscrowCreate";
|
||||||
|
jv[jss::Flags] = tfUniversal;
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[jss::Destination] = to.human();
|
||||||
|
jv[jss::Amount] = amount.getJson(0);
|
||||||
|
jv[sfFinishAfter.jsonName] =
|
||||||
|
cancelAfter.time_since_epoch().count() + 2;
|
||||||
|
return jv;
|
||||||
|
};
|
||||||
|
|
||||||
|
env (escrowCreate (alice, alice, XRP(333), env.now() + 2s));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
std::string escrowIndex;
|
||||||
|
{
|
||||||
|
// Request the escrow using owner and sequence.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = Json::objectValue;
|
||||||
|
jvParams[jss::escrow][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::escrow][jss::seq] = env.seq (alice) - 1;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jrr[jss::node][jss::Amount] == XRP(333).value().getText());
|
||||||
|
escrowIndex = jrr[jss::index].asString();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request the escrow by index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = escrowIndex;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jrr[jss::node][jss::Amount] == XRP(333).value().getText());
|
||||||
|
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed owner entry.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = Json::objectValue;
|
||||||
|
|
||||||
|
std::string const badAddress = makeBadAddress (alice.human());
|
||||||
|
jvParams[jss::escrow][jss::owner] = badAddress;
|
||||||
|
jvParams[jss::escrow][jss::seq] = env.seq (alice) - 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedOwner", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Missing owner.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = Json::objectValue;
|
||||||
|
jvParams[jss::escrow][jss::seq] = env.seq (alice) - 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Missing sequence.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = Json::objectValue;
|
||||||
|
jvParams[jss::escrow][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Non-integer sequence.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::escrow] = Json::objectValue;
|
||||||
|
jvParams[jss::escrow][jss::owner] = alice.human();
|
||||||
|
jvParams[jss::escrow][jss::seq] =
|
||||||
|
std::to_string (env.seq (alice) - 1);
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryGenerator()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Generator");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
|
||||||
|
// All generator requests are deprecated.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::generator] = 5;
|
||||||
|
jvParams[jss::ledger_hash] = to_string (env.closed()->info().hash);
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "deprecatedFeature", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryOffer()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Offer");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
Account const gw { "gateway" };
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
env.fund (XRP(10000), alice, gw);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env (offer (alice, USD (321), XRP (322)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
std::string offerIndex;
|
||||||
|
{
|
||||||
|
// Request the offer using owner and sequence.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = Json::objectValue;
|
||||||
|
jvParams[jss::offer][jss::account] = alice.human();
|
||||||
|
jvParams[jss::offer][jss::seq] = env.seq (alice) - 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
|
||||||
|
offerIndex = jrr[jss::index].asString();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request the offer using its index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = offerIndex;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed account entry.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = Json::objectValue;
|
||||||
|
|
||||||
|
std::string const badAddress = makeBadAddress (alice.human());
|
||||||
|
jvParams[jss::offer][jss::account] = badAddress;
|
||||||
|
jvParams[jss::offer][jss::seq] = env.seq (alice) - 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedAddress", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed offer object. Missing account member.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = Json::objectValue;
|
||||||
|
jvParams[jss::offer][jss::seq] = env.seq (alice) - 1;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed offer object. Missing seq member.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = Json::objectValue;
|
||||||
|
jvParams[jss::offer][jss::account] = alice.human();
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Malformed offer object. Non-integral seq member.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::offer] = Json::objectValue;
|
||||||
|
jvParams[jss::offer][jss::account] = alice.human();
|
||||||
|
jvParams[jss::offer][jss::seq] =
|
||||||
|
std::to_string (env.seq (alice) - 1);
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryPayChan()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Pay Chan");
|
||||||
|
using namespace test::jtx;
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
|
||||||
|
env.fund (XRP(10000), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// Lambda to create a PayChan.
|
||||||
|
auto payChanCreate = [] (
|
||||||
|
test::jtx::Account const& account,
|
||||||
|
test::jtx::Account const& to,
|
||||||
|
STAmount const& amount,
|
||||||
|
NetClock::duration const& settleDelay,
|
||||||
|
PublicKey const& pk)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = "PaymentChannelCreate";
|
||||||
|
jv[jss::Account] = account.human();
|
||||||
|
jv[jss::Destination] = to.human();
|
||||||
|
jv[jss::Amount] = amount.getJson (0);
|
||||||
|
jv[sfSettleDelay.jsonName] = settleDelay.count();
|
||||||
|
jv[sfPublicKey.jsonName] = strHex (pk.slice());
|
||||||
|
return jv;
|
||||||
|
};
|
||||||
|
|
||||||
|
env (payChanCreate (alice, env.master, XRP(57), 18s, alice.pk()));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
|
||||||
|
uint256 const payChanIndex {
|
||||||
|
keylet::payChan (alice, env.master, env.seq (alice) - 1).key};
|
||||||
|
{
|
||||||
|
// Request the payment channel using its index.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::payment_channel] = to_string (payChanIndex);
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
|
||||||
|
BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Request an index that is not a payment channel.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::payment_channel] = ledgerHash;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "entryNotFound", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryRippleState()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request RippleState");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
Account const alice {"alice"};
|
||||||
|
Account const gw { "gateway" };
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
env.fund(XRP(10000), alice, gw);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env.trust(USD(999), alice);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
env(pay (gw, alice, USD(97)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
{
|
||||||
|
// Request the trust line using the accounts and currency.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
|
||||||
|
BEAST_EXPECT(
|
||||||
|
jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state is not an object.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = "ripple_state";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state.currency is missing.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state accounts is not an array.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = 2;
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state one of the accounts is missing.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state more than 2 accounts.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][2u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state account[0] is not a string.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = 44;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state account[1] is not a string.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = 21;
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state account[0] == account[1].
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedRequest", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state malformed account[0].
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] =
|
||||||
|
makeBadAddress (alice.human());
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedAddress", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state malformed account[1].
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] =
|
||||||
|
makeBadAddress (gw.human());
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USD";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedAddress", "");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// ripple_state malformed currency.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::ripple_state] = Json::objectValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][0u] = alice.human();
|
||||||
|
jvParams[jss::ripple_state][jss::accounts][1u] = gw.human();
|
||||||
|
jvParams[jss::ripple_state][jss::currency] = "USDollars";
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "malformedCurrency", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testLedgerEntryUnknownOption()
|
||||||
|
{
|
||||||
|
testcase ("ledger_entry Request Unknown Option");
|
||||||
|
using namespace test::jtx;
|
||||||
|
Env env {*this};
|
||||||
|
|
||||||
|
std::string const ledgerHash {to_string (env.closed()->info().hash)};
|
||||||
|
|
||||||
|
// "features" is not an option supported by ledger_entry.
|
||||||
|
Json::Value jvParams;
|
||||||
|
jvParams[jss::features] = ledgerHash;
|
||||||
|
jvParams[jss::ledger_hash] = ledgerHash;
|
||||||
|
Json::Value const jrr = env.rpc (
|
||||||
|
"json", "ledger_entry", to_string (jvParams))[jss::result];
|
||||||
|
checkErrorValue (jrr, "unknownOption", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief ledger RPC requests as a way to drive
|
/// @brief ledger RPC requests as a way to drive
|
||||||
@@ -298,6 +968,7 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
/// RPC request.
|
/// RPC request.
|
||||||
void testLookupLedger()
|
void testLookupLedger()
|
||||||
{
|
{
|
||||||
|
testcase ("Lookup ledger");
|
||||||
using namespace test::jtx;
|
using namespace test::jtx;
|
||||||
Env env {*this, FeatureBitset{}}; // hashes requested below assume
|
Env env {*this, FeatureBitset{}}; // hashes requested below assume
|
||||||
//no amendments
|
//no amendments
|
||||||
@@ -462,7 +1133,6 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testNoQueue()
|
void testNoQueue()
|
||||||
@@ -660,10 +1330,9 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(tx[jss::TransactionType] == "AccountSet");
|
BEAST_EXPECT(tx[jss::TransactionType] == "AccountSet");
|
||||||
BEAST_EXPECT(tx[jss::hash] == txid2);
|
BEAST_EXPECT(tx[jss::hash] == txid2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testLedgerAccountsType()
|
void testLedgerAccountsOption()
|
||||||
{
|
{
|
||||||
testcase("Ledger Request, Accounts Option");
|
testcase("Ledger Request, Accounts Option");
|
||||||
using namespace test::jtx;
|
using namespace test::jtx;
|
||||||
@@ -674,30 +1343,33 @@ class LedgerRPC_test : public beast::unit_test::suite
|
|||||||
|
|
||||||
std::string index;
|
std::string index;
|
||||||
{
|
{
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
jvParams[jss::ledger_index] = 3u;
|
jvParams[jss::ledger_index] = 3u;
|
||||||
jvParams[jss::accounts] = true;
|
jvParams[jss::accounts] = true;
|
||||||
jvParams[jss::expand] = true;
|
jvParams[jss::expand] = true;
|
||||||
jvParams[jss::type] = "hashes";
|
jvParams[jss::type] = "hashes";
|
||||||
auto const jrr = env.rpc ( "json", "ledger", to_string(jvParams) ) [jss::result];
|
auto const jrr = env.rpc (
|
||||||
BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
|
"json", "ledger", to_string(jvParams) )[jss::result];
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
|
BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"]
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
|
||||||
== "LedgerHashes");
|
BEAST_EXPECT(
|
||||||
index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
|
jrr[jss::ledger][jss::accountState][0u]["LedgerEntryType"]
|
||||||
|
== "LedgerHashes");
|
||||||
|
index = jrr[jss::ledger][jss::accountState][0u]["index"].asString();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Json::Value jvParams;
|
Json::Value jvParams;
|
||||||
jvParams[jss::ledger_index] = 3u;
|
jvParams[jss::ledger_index] = 3u;
|
||||||
jvParams[jss::accounts] = true;
|
jvParams[jss::accounts] = true;
|
||||||
jvParams[jss::expand] = false;
|
jvParams[jss::expand] = false;
|
||||||
jvParams[jss::type] = "hashes";
|
jvParams[jss::type] = "hashes";
|
||||||
auto const jrr = env.rpc ( "json", "ledger", to_string(jvParams) ) [jss::result];
|
auto const jrr = env.rpc (
|
||||||
BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
|
"json", "ledger", to_string(jvParams) )[jss::result];
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
|
BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState));
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray());
|
||||||
BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 1u);
|
||||||
|
BEAST_EXPECT(jrr[jss::ledger][jss::accountState][0u] == index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,17 +1379,23 @@ public:
|
|||||||
testLedgerRequest();
|
testLedgerRequest();
|
||||||
testBadInput();
|
testBadInput();
|
||||||
testLedgerCurrent();
|
testLedgerCurrent();
|
||||||
testAccountRoot();
|
testMissingLedgerEntryLedgerHash();
|
||||||
testLedgerFull();
|
testLedgerFull();
|
||||||
testLedgerFullNonAdmin();
|
testLedgerFullNonAdmin();
|
||||||
testLedgerAccounts();
|
testLedgerAccounts();
|
||||||
testMalformedAccountRoot();
|
testLedgerEntryAccountRoot();
|
||||||
testNotFoundAccountRoot();
|
testLedgerEntryCheck();
|
||||||
testAccountRootFromIndex();
|
testLedgerEntryDirectory();
|
||||||
|
testLedgerEntryEscrow();
|
||||||
|
testLedgerEntryGenerator();
|
||||||
|
testLedgerEntryOffer();
|
||||||
|
testLedgerEntryPayChan();
|
||||||
|
testLedgerEntryRippleState();
|
||||||
|
testLedgerEntryUnknownOption();
|
||||||
testLookupLedger();
|
testLookupLedger();
|
||||||
testNoQueue();
|
testNoQueue();
|
||||||
testQueue();
|
testQueue();
|
||||||
testLedgerAccountsType();
|
testLedgerAccountsOption();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user