mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Entries in the ledger are located using 256-bit locators. The locators are calculated using a wide range of parameters specific to the entry whose locator we are calculating (e.g. an account's locator is derived from the account's address, whereas the locator for an offer is derived from the account and the offer sequence.) Keylets enhance type safety during lookup and make the code more robust, so this commit removes most of the earlier code, which used naked uint256 values.
2180 lines
92 KiB
C++
2180 lines
92 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2016 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 <ripple/beast/unit_test.h>
|
|
#include <ripple/protocol/SField.h>
|
|
#include <ripple/protocol/jss.h>
|
|
#include <cstdlib>
|
|
#include <test/jtx.h>
|
|
|
|
#include <ripple/rpc/GRPCHandlers.h>
|
|
#include <ripple/rpc/impl/RPCHelpers.h>
|
|
#include <test/rpc/GRPCTestClientBase.h>
|
|
|
|
namespace ripple {
|
|
|
|
class AccountTxPaging_test : public beast::unit_test::suite
|
|
{
|
|
bool
|
|
checkTransaction(Json::Value const& tx, int sequence, int ledger)
|
|
{
|
|
return (
|
|
tx[jss::tx][jss::Sequence].asInt() == sequence &&
|
|
tx[jss::tx][jss::ledger_index].asInt() == ledger);
|
|
}
|
|
|
|
auto
|
|
next(
|
|
test::jtx::Env& env,
|
|
test::jtx::Account const& account,
|
|
int ledger_min,
|
|
int ledger_max,
|
|
int limit,
|
|
bool forward,
|
|
Json::Value const& marker = Json::nullValue)
|
|
{
|
|
Json::Value jvc;
|
|
jvc[jss::account] = account.human();
|
|
jvc[jss::ledger_index_min] = ledger_min;
|
|
jvc[jss::ledger_index_max] = ledger_max;
|
|
jvc[jss::forward] = forward;
|
|
jvc[jss::limit] = limit;
|
|
if (marker)
|
|
jvc[jss::marker] = marker;
|
|
|
|
return env.rpc("json", "account_tx", to_string(jvc))[jss::result];
|
|
}
|
|
|
|
void
|
|
testAccountTxPaging()
|
|
{
|
|
testcase("Paging for Single Account");
|
|
using namespace test::jtx;
|
|
|
|
Env env(*this);
|
|
Account A1{"A1"};
|
|
Account A2{"A2"};
|
|
Account A3{"A3"};
|
|
|
|
env.fund(XRP(10000), A1, A2, A3);
|
|
env.close();
|
|
|
|
env.trust(A3["USD"](1000), A1);
|
|
env.trust(A2["USD"](1000), A1);
|
|
env.trust(A3["USD"](1000), A2);
|
|
env.close();
|
|
|
|
for (auto i = 0; i < 5; ++i)
|
|
{
|
|
env(pay(A2, A1, A2["USD"](2)));
|
|
env(pay(A3, A1, A3["USD"](2)));
|
|
env(offer(A1, XRP(11), A1["USD"](1)));
|
|
env(offer(A2, XRP(10), A2["USD"](1)));
|
|
env(offer(A3, XRP(9), A3["USD"](1)));
|
|
env.close();
|
|
}
|
|
|
|
/* The sequence/ledger for A3 are as follows:
|
|
* seq ledger_index
|
|
* 3 ----> 3
|
|
* 1 ----> 3
|
|
* 2 ----> 4
|
|
* 2 ----> 4
|
|
* 2 ----> 5
|
|
* 3 ----> 5
|
|
* 4 ----> 6
|
|
* 5 ----> 6
|
|
* 6 ----> 7
|
|
* 7 ----> 7
|
|
* 8 ----> 8
|
|
* 9 ----> 8
|
|
* 10 ----> 9
|
|
* 11 ----> 9
|
|
*/
|
|
|
|
// page through the results in several ways.
|
|
{
|
|
// limit = 2, 3 batches giving the first 6 txs
|
|
auto jrr = next(env, A3, 2, 5, 2, true);
|
|
auto txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 3, 3));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 2, 5, 2, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 4));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 2, 5, 2, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 5, 5));
|
|
BEAST_EXPECT(!jrr[jss::marker]);
|
|
}
|
|
|
|
{
|
|
// limit 1, 3 requests giving the first 3 txs
|
|
auto jrr = next(env, A3, 3, 9, 1, true);
|
|
auto txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 1, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 1, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
// continue with limit 3, to end of all txs
|
|
jrr = next(env, A3, 3, 9, 3, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 5, 5));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 6, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 7, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 8, 7));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 9, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 10, 8));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 11, 8));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, true, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 12, 9));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 13, 9));
|
|
BEAST_EXPECT(!jrr[jss::marker]);
|
|
}
|
|
|
|
{
|
|
// limit 2, descending, 2 batches giving last 4 txs
|
|
auto jrr = next(env, A3, 3, 9, 2, false);
|
|
auto txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 13, 9));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 12, 9));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 2, false, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 11, 8));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 10, 8));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
// continue with limit 3 until all txs have been seen
|
|
jrr = next(env, A3, 3, 9, 3, false, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 9, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 8, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 7, 6));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, false, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 6, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 5, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 4, 5));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, false, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 3, 3));
|
|
if (!BEAST_EXPECT(jrr[jss::marker]))
|
|
return;
|
|
|
|
jrr = next(env, A3, 3, 9, 3, false, jrr[jss::marker]);
|
|
txs = jrr[jss::transactions];
|
|
if (!BEAST_EXPECT(txs.isArray() && txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
BEAST_EXPECT(!jrr[jss::marker]);
|
|
}
|
|
}
|
|
|
|
class GrpcAccountTxClient : public test::GRPCTestClientBase
|
|
{
|
|
public:
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest request;
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse reply;
|
|
|
|
explicit GrpcAccountTxClient(std::string const& port)
|
|
: GRPCTestClientBase(port)
|
|
{
|
|
}
|
|
|
|
void
|
|
AccountTx()
|
|
{
|
|
status =
|
|
stub_->GetAccountTransactionHistory(&context, request, &reply);
|
|
}
|
|
};
|
|
|
|
bool
|
|
checkTransaction(
|
|
org::xrpl::rpc::v1::GetTransactionResponse const& tx,
|
|
int sequence,
|
|
int ledger)
|
|
{
|
|
return (
|
|
tx.transaction().sequence().value() == sequence &&
|
|
tx.ledger_index() == ledger);
|
|
}
|
|
|
|
std::pair<
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
|
|
grpc::Status>
|
|
nextBinary(
|
|
std::string grpcPort,
|
|
test::jtx::Env& env,
|
|
std::string const& account = "",
|
|
int ledger_min = -1,
|
|
int ledger_max = -1,
|
|
int limit = -1,
|
|
bool forward = false,
|
|
org::xrpl::rpc::v1::Marker* marker = nullptr)
|
|
{
|
|
GrpcAccountTxClient client{grpcPort};
|
|
auto& request = client.request;
|
|
if (account != "")
|
|
request.mutable_account()->set_address(account);
|
|
if (ledger_min != -1)
|
|
request.mutable_ledger_range()->set_ledger_index_min(ledger_min);
|
|
if (ledger_max != -1)
|
|
request.mutable_ledger_range()->set_ledger_index_max(ledger_max);
|
|
request.set_forward(forward);
|
|
request.set_binary(true);
|
|
if (limit != -1)
|
|
request.set_limit(limit);
|
|
if (marker)
|
|
{
|
|
*request.mutable_marker() = *marker;
|
|
}
|
|
|
|
client.AccountTx();
|
|
return {client.reply, client.status};
|
|
}
|
|
|
|
std::pair<
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
|
|
grpc::Status>
|
|
next(
|
|
std::string grpcPort,
|
|
test::jtx::Env& env,
|
|
std::string const& account = "",
|
|
int ledger_min = -1,
|
|
int ledger_max = -1,
|
|
int limit = -1,
|
|
bool forward = false,
|
|
org::xrpl::rpc::v1::Marker* marker = nullptr)
|
|
{
|
|
GrpcAccountTxClient client{grpcPort};
|
|
auto& request = client.request;
|
|
if (account != "")
|
|
request.mutable_account()->set_address(account);
|
|
if (ledger_min != -1)
|
|
request.mutable_ledger_range()->set_ledger_index_min(ledger_min);
|
|
if (ledger_max != -1)
|
|
request.mutable_ledger_range()->set_ledger_index_max(ledger_max);
|
|
request.set_forward(forward);
|
|
if (limit != -1)
|
|
request.set_limit(limit);
|
|
if (marker)
|
|
{
|
|
*request.mutable_marker() = *marker;
|
|
}
|
|
|
|
client.AccountTx();
|
|
return {client.reply, client.status};
|
|
}
|
|
|
|
std::pair<
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
|
|
grpc::Status>
|
|
nextWithSeq(
|
|
std::string grpcPort,
|
|
test::jtx::Env& env,
|
|
std::string const& account = "",
|
|
int ledger_seq = -1,
|
|
int limit = -1,
|
|
bool forward = false,
|
|
org::xrpl::rpc::v1::Marker* marker = nullptr)
|
|
{
|
|
GrpcAccountTxClient client{grpcPort};
|
|
auto& request = client.request;
|
|
if (account != "")
|
|
request.mutable_account()->set_address(account);
|
|
if (ledger_seq != -1)
|
|
request.mutable_ledger_specifier()->set_sequence(ledger_seq);
|
|
request.set_forward(forward);
|
|
if (limit != -1)
|
|
request.set_limit(limit);
|
|
if (marker)
|
|
{
|
|
*request.mutable_marker() = *marker;
|
|
}
|
|
|
|
client.AccountTx();
|
|
return {client.reply, client.status};
|
|
}
|
|
|
|
std::pair<
|
|
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
|
|
grpc::Status>
|
|
nextWithHash(
|
|
std::string grpcPort,
|
|
test::jtx::Env& env,
|
|
std::string const& account = "",
|
|
uint256 const& hash = beast::zero,
|
|
int limit = -1,
|
|
bool forward = false,
|
|
org::xrpl::rpc::v1::Marker* marker = nullptr)
|
|
{
|
|
GrpcAccountTxClient client{grpcPort};
|
|
auto& request = client.request;
|
|
if (account != "")
|
|
request.mutable_account()->set_address(account);
|
|
if (hash != beast::zero)
|
|
request.mutable_ledger_specifier()->set_hash(
|
|
hash.data(), hash.size());
|
|
request.set_forward(forward);
|
|
if (limit != -1)
|
|
request.set_limit(limit);
|
|
if (marker)
|
|
{
|
|
*request.mutable_marker() = *marker;
|
|
}
|
|
|
|
client.AccountTx();
|
|
return {client.reply, client.status};
|
|
}
|
|
|
|
void
|
|
testAccountTxParametersGrpc()
|
|
{
|
|
testcase("Test Account_tx Grpc");
|
|
|
|
using namespace test::jtx;
|
|
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
|
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
|
Env env(*this, std::move(config));
|
|
|
|
Account A1{"A1"};
|
|
env.fund(XRP(10000), A1);
|
|
env.close();
|
|
|
|
// Ledger 3 has the two txs associated with funding the account
|
|
// All other ledgers have no txs
|
|
|
|
auto hasTxs = [](auto res) {
|
|
return res.second.error_code() == 0 &&
|
|
(res.first.transactions().size() == 2) &&
|
|
//(res.transactions()[0u].transaction().has_account_set()) &&
|
|
(res.first.transactions()[1u].transaction().has_payment());
|
|
};
|
|
auto noTxs = [](auto res) {
|
|
return res.second.error_code() == 0 &&
|
|
(res.first.transactions().size() == 0);
|
|
};
|
|
|
|
auto isErr = [](auto res, auto expect) {
|
|
return res.second.error_code() == expect;
|
|
};
|
|
|
|
BEAST_EXPECT(
|
|
isErr(next(grpcPort, env, ""), grpc::StatusCode::INVALID_ARGUMENT));
|
|
|
|
BEAST_EXPECT(isErr(
|
|
next(grpcPort, env, "0xDEADBEEF"),
|
|
grpc::StatusCode::INVALID_ARGUMENT));
|
|
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human())));
|
|
|
|
// Ledger min/max index
|
|
{
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human())));
|
|
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human(), 0, 100)));
|
|
|
|
BEAST_EXPECT(noTxs(next(grpcPort, env, A1.human(), 1, 2)));
|
|
|
|
BEAST_EXPECT(isErr(
|
|
next(grpcPort, env, A1.human(), 2, 1),
|
|
grpc::StatusCode::INVALID_ARGUMENT));
|
|
}
|
|
|
|
// Ledger index min only
|
|
{
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human(), -1)));
|
|
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human(), 1)));
|
|
|
|
BEAST_EXPECT(isErr(
|
|
next(grpcPort, env, A1.human(), env.current()->info().seq),
|
|
grpc::StatusCode::INVALID_ARGUMENT));
|
|
}
|
|
|
|
// Ledger index max only
|
|
{
|
|
BEAST_EXPECT(hasTxs(next(grpcPort, env, A1.human(), -1, -1)));
|
|
|
|
BEAST_EXPECT(hasTxs(next(
|
|
grpcPort, env, A1.human(), -1, env.current()->info().seq)));
|
|
|
|
BEAST_EXPECT(hasTxs(
|
|
next(grpcPort, env, A1.human(), -1, env.closed()->info().seq)));
|
|
|
|
BEAST_EXPECT(noTxs(next(
|
|
grpcPort, env, A1.human(), -1, env.closed()->info().seq - 1)));
|
|
}
|
|
// Ledger Sequence
|
|
{
|
|
BEAST_EXPECT(hasTxs(nextWithSeq(
|
|
grpcPort, env, A1.human(), env.closed()->info().seq)));
|
|
|
|
BEAST_EXPECT(noTxs(nextWithSeq(
|
|
grpcPort, env, A1.human(), env.closed()->info().seq - 1)));
|
|
|
|
BEAST_EXPECT(isErr(
|
|
nextWithSeq(
|
|
grpcPort, env, A1.human(), env.current()->info().seq),
|
|
grpc::StatusCode::INVALID_ARGUMENT));
|
|
|
|
BEAST_EXPECT(isErr(
|
|
nextWithSeq(
|
|
grpcPort, env, A1.human(), env.current()->info().seq + 1),
|
|
grpc::StatusCode::NOT_FOUND));
|
|
}
|
|
|
|
// Ledger Hash
|
|
{
|
|
BEAST_EXPECT(hasTxs(nextWithHash(
|
|
grpcPort, env, A1.human(), env.closed()->info().hash)));
|
|
|
|
BEAST_EXPECT(noTxs(nextWithHash(
|
|
grpcPort, env, A1.human(), env.closed()->info().parentHash)));
|
|
}
|
|
}
|
|
|
|
struct TxCheck
|
|
{
|
|
uint32_t sequence;
|
|
uint32_t ledgerIndex;
|
|
std::string hash;
|
|
std::function<bool(org::xrpl::rpc::v1::Transaction const& res)>
|
|
checkTxn;
|
|
};
|
|
|
|
void
|
|
testAccountTxContentsGrpc()
|
|
{
|
|
testcase("Test AccountTx context grpc");
|
|
// Get results for all transaction types that can be associated
|
|
// with an account. Start by generating all transaction types.
|
|
using namespace test::jtx;
|
|
using namespace std::chrono_literals;
|
|
|
|
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
|
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
|
Env env(*this, std::move(config));
|
|
// Set time to this value (or greater) to get delivered_amount in meta
|
|
env.timeKeeper().set(NetClock::time_point{446000001s});
|
|
Account const alice{"alice"};
|
|
Account const alie{"alie"};
|
|
Account const gw{"gw"};
|
|
auto const USD{gw["USD"]};
|
|
|
|
std::vector<std::shared_ptr<STTx const>> txns;
|
|
|
|
env.fund(XRP(1000000), alice, gw);
|
|
env.close();
|
|
|
|
// AccountSet
|
|
env(noop(alice));
|
|
|
|
txns.emplace_back(env.tx());
|
|
// Payment
|
|
env(pay(alice, gw, XRP(100)), stag(42), dtag(24), last_ledger_seq(20));
|
|
|
|
txns.emplace_back(env.tx());
|
|
// Regular key set
|
|
env(regkey(alice, alie));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
// Trust and Offers
|
|
env(trust(alice, USD(200)), sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
std::uint32_t const offerSeq{env.seq(alice)};
|
|
env(offer(alice, USD(50), XRP(150)), sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
env.close();
|
|
|
|
{
|
|
Json::Value cancelOffer;
|
|
cancelOffer[jss::Account] = alice.human();
|
|
cancelOffer[jss::OfferSequence] = offerSeq;
|
|
cancelOffer[jss::TransactionType] = jss::OfferCancel;
|
|
env(cancelOffer, sig(alie));
|
|
}
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
|
|
// SignerListSet
|
|
env(signers(alice, 1, {{"bogie", 1}, {"demon", 1}, {gw, 1}}),
|
|
sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
// Escrow
|
|
{
|
|
// Create an escrow. Requires either a CancelAfter or FinishAfter.
|
|
auto escrow = [](Account const& account,
|
|
Account const& to,
|
|
STAmount const& amount) {
|
|
Json::Value escro;
|
|
escro[jss::TransactionType] = jss::EscrowCreate;
|
|
escro[jss::Flags] = tfUniversal;
|
|
escro[jss::Account] = account.human();
|
|
escro[jss::Destination] = to.human();
|
|
escro[jss::Amount] = amount.getJson(JsonOptions::none);
|
|
return escro;
|
|
};
|
|
|
|
NetClock::time_point const nextTime{env.now() + 2s};
|
|
|
|
Json::Value escrowWithFinish{escrow(alice, alice, XRP(500))};
|
|
escrowWithFinish[sfFinishAfter.jsonName] =
|
|
nextTime.time_since_epoch().count();
|
|
|
|
std::uint32_t const escrowFinishSeq{env.seq(alice)};
|
|
env(escrowWithFinish, sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
Json::Value escrowWithCancel{escrow(alice, alice, XRP(500))};
|
|
escrowWithCancel[sfFinishAfter.jsonName] =
|
|
nextTime.time_since_epoch().count();
|
|
escrowWithCancel[sfCancelAfter.jsonName] =
|
|
nextTime.time_since_epoch().count() + 1;
|
|
|
|
std::uint32_t const escrowCancelSeq{env.seq(alice)};
|
|
env(escrowWithCancel, sig(alie));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
{
|
|
Json::Value escrowFinish;
|
|
escrowFinish[jss::TransactionType] = jss::EscrowFinish;
|
|
escrowFinish[jss::Flags] = tfUniversal;
|
|
escrowFinish[jss::Account] = alice.human();
|
|
escrowFinish[sfOwner.jsonName] = alice.human();
|
|
escrowFinish[sfOfferSequence.jsonName] = escrowFinishSeq;
|
|
env(escrowFinish, sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
}
|
|
{
|
|
Json::Value escrowCancel;
|
|
escrowCancel[jss::TransactionType] = jss::EscrowCancel;
|
|
escrowCancel[jss::Flags] = tfUniversal;
|
|
escrowCancel[jss::Account] = alice.human();
|
|
escrowCancel[sfOwner.jsonName] = alice.human();
|
|
escrowCancel[sfOfferSequence.jsonName] = escrowCancelSeq;
|
|
env(escrowCancel, sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
}
|
|
env.close();
|
|
}
|
|
|
|
// PayChan
|
|
{
|
|
std::uint32_t payChanSeq{env.seq(alice)};
|
|
Json::Value payChanCreate;
|
|
payChanCreate[jss::TransactionType] = jss::PaymentChannelCreate;
|
|
payChanCreate[jss::Flags] = tfUniversal;
|
|
payChanCreate[jss::Account] = alice.human();
|
|
payChanCreate[jss::Destination] = gw.human();
|
|
payChanCreate[jss::Amount] =
|
|
XRP(500).value().getJson(JsonOptions::none);
|
|
payChanCreate[sfSettleDelay.jsonName] =
|
|
NetClock::duration{100s}.count();
|
|
payChanCreate[sfPublicKey.jsonName] = strHex(alice.pk().slice());
|
|
env(payChanCreate, sig(alie));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
std::string const payChanIndex{
|
|
strHex(keylet::payChan(alice, gw, payChanSeq).key)};
|
|
|
|
{
|
|
Json::Value payChanFund;
|
|
payChanFund[jss::TransactionType] = jss::PaymentChannelFund;
|
|
payChanFund[jss::Flags] = tfUniversal;
|
|
payChanFund[jss::Account] = alice.human();
|
|
payChanFund[sfPayChannel.jsonName] = payChanIndex;
|
|
payChanFund[jss::Amount] =
|
|
XRP(200).value().getJson(JsonOptions::none);
|
|
env(payChanFund, sig(alie));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
}
|
|
{
|
|
Json::Value payChanClaim;
|
|
payChanClaim[jss::TransactionType] = jss::PaymentChannelClaim;
|
|
payChanClaim[jss::Flags] = tfClose;
|
|
payChanClaim[jss::Account] = gw.human();
|
|
payChanClaim[sfPayChannel.jsonName] = payChanIndex;
|
|
payChanClaim[sfPublicKey.jsonName] = strHex(alice.pk().slice());
|
|
env(payChanClaim);
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
}
|
|
}
|
|
|
|
// Check
|
|
{
|
|
auto const aliceCheckId = keylet::check(alice, env.seq(alice)).key;
|
|
env(check::create(alice, gw, XRP(300)), sig(alie));
|
|
|
|
auto txn = env.tx();
|
|
auto const gwCheckId = keylet::check(gw, env.seq(gw)).key;
|
|
env(check::create(gw, alice, XRP(200)));
|
|
env.close();
|
|
|
|
// need to switch the order of the previous 2 txns, since they are
|
|
// in the same ledger and account_tx returns them in a different
|
|
// order
|
|
txns.emplace_back(env.tx());
|
|
txns.emplace_back(txn);
|
|
env(check::cash(alice, gwCheckId, XRP(200)), sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
env(check::cancel(alice, aliceCheckId), sig(alie));
|
|
|
|
txns.emplace_back(env.tx());
|
|
env.close();
|
|
}
|
|
|
|
// Deposit preauthorization.
|
|
env(deposit::auth(alice, gw), sig(alie));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
// Multi Sig with memo
|
|
auto const baseFee = env.current()->fees().base;
|
|
env(noop(alice),
|
|
msig(gw),
|
|
fee(2 * baseFee),
|
|
memo("data", "format", "type"));
|
|
env.close();
|
|
|
|
txns.emplace_back(env.tx());
|
|
if (!BEAST_EXPECT(txns.size() == 20))
|
|
return;
|
|
// Setup is done. Look at the transactions returned by account_tx.
|
|
|
|
static const TxCheck txCheck[]{
|
|
{21,
|
|
15,
|
|
strHex(txns[txns.size() - 1]->getTransactionID()),
|
|
[this, &txns](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 1]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_account_set()) &&
|
|
BEAST_EXPECT(res.has_fee()) &&
|
|
BEAST_EXPECT(res.fee().drops() == 20) &&
|
|
BEAST_EXPECT(res.memos_size() == 1) &&
|
|
BEAST_EXPECT(res.memos(0).has_memo_data()) &&
|
|
BEAST_EXPECT(res.memos(0).memo_data().value() == "data") &&
|
|
BEAST_EXPECT(res.memos(0).has_memo_format()) &&
|
|
BEAST_EXPECT(
|
|
res.memos(0).memo_format().value() == "format") &&
|
|
BEAST_EXPECT(res.memos(0).has_memo_type()) &&
|
|
BEAST_EXPECT(res.memos(0).memo_type().value() == "type") &&
|
|
BEAST_EXPECT(res.has_signing_public_key()) &&
|
|
BEAST_EXPECT(res.signing_public_key().value() == "") &&
|
|
BEAST_EXPECT(res.signers_size() == 1) &&
|
|
BEAST_EXPECT(res.signers(0).has_account()) &&
|
|
BEAST_EXPECT(
|
|
res.signers(0).account().value().address() ==
|
|
txnJson["Signers"][0u]["Signer"]["Account"]) &&
|
|
BEAST_EXPECT(res.signers(0).has_transaction_signature()) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.signers(0)
|
|
.transaction_signature()
|
|
.value()) ==
|
|
txnJson["Signers"][0u]["Signer"]["TxnSignature"]) &&
|
|
BEAST_EXPECT(res.signers(0).has_signing_public_key()) &&
|
|
BEAST_EXPECT(
|
|
strHex(
|
|
res.signers(0).signing_public_key().value()) ==
|
|
txnJson["Signers"][0u]["Signer"]["SigningPubKey"]);
|
|
}},
|
|
{20,
|
|
14,
|
|
strHex(txns[txns.size() - 2]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
return BEAST_EXPECT(res.has_deposit_preauth()) &&
|
|
BEAST_EXPECT(
|
|
res.deposit_preauth()
|
|
.authorize()
|
|
.value()
|
|
.address() ==
|
|
// TODO do them all like this
|
|
txns[txns.size() - 2]->getJson(
|
|
JsonOptions::none)["Authorize"]);
|
|
}},
|
|
{19,
|
|
13,
|
|
strHex(txns[txns.size() - 3]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
return BEAST_EXPECT(res.has_check_cancel()) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.check_cancel().check_id().value()) ==
|
|
|
|
txns[txns.size() - 3]->getJson(
|
|
JsonOptions::none)["CheckID"]);
|
|
}},
|
|
{18,
|
|
13,
|
|
strHex(txns[txns.size() - 4]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 4]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_check_cash()) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.check_cash().check_id().value()) ==
|
|
txnJson["CheckID"]) &&
|
|
BEAST_EXPECT(res.check_cash()
|
|
.amount()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.check_cash()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt());
|
|
}},
|
|
{17,
|
|
12,
|
|
strHex(txns[txns.size() - 5]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 5]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_check_create()) &&
|
|
BEAST_EXPECT(
|
|
res.check_create()
|
|
.destination()
|
|
.value()
|
|
.address() == txnJson["Destination"]) &&
|
|
BEAST_EXPECT(res.check_create()
|
|
.send_max()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.check_create()
|
|
.send_max()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["SendMax"].asUInt());
|
|
}},
|
|
{5,
|
|
12,
|
|
strHex(txns[txns.size() - 6]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 6]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_check_create()) &&
|
|
BEAST_EXPECT(
|
|
res.check_create()
|
|
.destination()
|
|
.value()
|
|
.address() == txnJson["Destination"]) &&
|
|
BEAST_EXPECT(res.check_create()
|
|
.send_max()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.check_create()
|
|
.send_max()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() ==
|
|
|
|
txnJson["SendMax"].asUInt());
|
|
}},
|
|
{4,
|
|
11,
|
|
strHex(txns[txns.size() - 7]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 7]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_payment_channel_claim()) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.payment_channel_claim()
|
|
.channel()
|
|
.value()) == txnJson["Channel"]) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.payment_channel_claim()
|
|
.public_key()
|
|
.value()) == txnJson["PublicKey"]);
|
|
}},
|
|
{16,
|
|
10,
|
|
strHex(txns[txns.size() - 8]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 8]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_payment_channel_fund()) &&
|
|
BEAST_EXPECT(
|
|
strHex(
|
|
res.payment_channel_fund().channel().value()) ==
|
|
txnJson["Channel"]) &&
|
|
BEAST_EXPECT(res.payment_channel_fund()
|
|
.amount()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.payment_channel_fund()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt());
|
|
}},
|
|
{15,
|
|
9,
|
|
strHex(txns[txns.size() - 9]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 9]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_payment_channel_create()) &&
|
|
BEAST_EXPECT(res.payment_channel_create()
|
|
.amount()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.payment_channel_create()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.payment_channel_create()
|
|
.destination()
|
|
.value()
|
|
.address() == txnJson["Destination"]) &&
|
|
BEAST_EXPECT(
|
|
res.payment_channel_create()
|
|
.settle_delay()
|
|
.value() == txnJson["SettleDelay"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
strHex(res.payment_channel_create()
|
|
.public_key()
|
|
.value()) == txnJson["PublicKey"]);
|
|
}},
|
|
{14,
|
|
8,
|
|
strHex(txns[txns.size() - 10]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 10]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_escrow_cancel()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_cancel().owner().value().address() ==
|
|
txnJson["Owner"]) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_cancel().offer_sequence().value() ==
|
|
txnJson["OfferSequence"].asUInt()
|
|
|
|
);
|
|
}},
|
|
{13,
|
|
8,
|
|
strHex(txns[txns.size() - 11]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 11]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_escrow_finish()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_finish().owner().value().address() ==
|
|
txnJson["Owner"]) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_finish().offer_sequence().value() ==
|
|
txnJson["OfferSequence"].asUInt()
|
|
|
|
);
|
|
}},
|
|
{12,
|
|
7,
|
|
strHex(txns[txns.size() - 12]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 12]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_escrow_create()) &&
|
|
BEAST_EXPECT(res.escrow_create()
|
|
.amount()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create()
|
|
.destination()
|
|
.value()
|
|
.address() == txnJson["Destination"]) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create().cancel_after().value() ==
|
|
txnJson["CancelAfter"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create().finish_after().value() ==
|
|
txnJson["FinishAfter"].asUInt());
|
|
}},
|
|
{11,
|
|
7,
|
|
strHex(txns[txns.size() - 13]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 13]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_escrow_create()) &&
|
|
BEAST_EXPECT(res.escrow_create()
|
|
.amount()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create()
|
|
.destination()
|
|
.value()
|
|
.address() == txnJson["Destination"]) &&
|
|
BEAST_EXPECT(
|
|
res.escrow_create().finish_after().value() ==
|
|
txnJson["FinishAfter"].asUInt());
|
|
}},
|
|
{10,
|
|
7,
|
|
strHex(txns[txns.size() - 14]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 14]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_signer_list_set()) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set().signer_quorum().value() ==
|
|
txnJson["SignerQuorum"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set().signer_entries().size() ==
|
|
3) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[0]
|
|
.account()
|
|
.value()
|
|
.address() ==
|
|
txnJson["SignerEntries"][0u]["SignerEntry"]
|
|
["Account"]) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[0]
|
|
.signer_weight()
|
|
.value() ==
|
|
txnJson["SignerEntries"][0u]["SignerEntry"]
|
|
["SignerWeight"]
|
|
.asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[1]
|
|
.account()
|
|
.value()
|
|
.address() ==
|
|
txnJson["SignerEntries"][1u]["SignerEntry"]
|
|
["Account"]) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[1]
|
|
.signer_weight()
|
|
.value() ==
|
|
txnJson["SignerEntries"][1u]["SignerEntry"]
|
|
["SignerWeight"]
|
|
.asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[2]
|
|
.account()
|
|
.value()
|
|
.address() ==
|
|
txnJson["SignerEntries"][2u]["SignerEntry"]
|
|
["Account"]) &&
|
|
BEAST_EXPECT(
|
|
res.signer_list_set()
|
|
.signer_entries()[2]
|
|
.signer_weight()
|
|
.value() ==
|
|
txnJson["SignerEntries"][2u]["SignerEntry"]
|
|
["SignerWeight"]
|
|
.asUInt());
|
|
}},
|
|
{9,
|
|
6,
|
|
strHex(txns[txns.size() - 15]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 15]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_offer_cancel()) &&
|
|
BEAST_EXPECT(
|
|
res.offer_cancel().offer_sequence().value() ==
|
|
txnJson["OfferSequence"].asUInt());
|
|
}},
|
|
{8,
|
|
5,
|
|
strHex(txns[txns.size() - 16]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 16]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_offer_create()) &&
|
|
BEAST_EXPECT(res.offer_create()
|
|
.taker_gets()
|
|
.value()
|
|
.has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.offer_create()
|
|
.taker_gets()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["TakerGets"].asUInt()) &&
|
|
BEAST_EXPECT(res.offer_create()
|
|
.taker_pays()
|
|
.value()
|
|
.has_issued_currency_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.offer_create()
|
|
.taker_pays()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.currency()
|
|
.name() == txnJson["TakerPays"]["currency"]) &&
|
|
BEAST_EXPECT(
|
|
res.offer_create()
|
|
.taker_pays()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.value() == txnJson["TakerPays"]["value"]) &&
|
|
BEAST_EXPECT(
|
|
res.offer_create()
|
|
.taker_pays()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.issuer()
|
|
.address() == txnJson["TakerPays"]["issuer"]);
|
|
}},
|
|
{7,
|
|
5,
|
|
strHex(txns[txns.size() - 17]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 17]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_trust_set()) &&
|
|
BEAST_EXPECT(res.trust_set()
|
|
.limit_amount()
|
|
.value()
|
|
.has_issued_currency_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.trust_set()
|
|
.limit_amount()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.currency()
|
|
.name() ==
|
|
txnJson["LimitAmount"]["currency"]) &&
|
|
BEAST_EXPECT(
|
|
res.trust_set()
|
|
.limit_amount()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.value() == txnJson["LimitAmount"]["value"]) &&
|
|
BEAST_EXPECT(
|
|
res.trust_set()
|
|
.limit_amount()
|
|
.value()
|
|
.issued_currency_amount()
|
|
.issuer()
|
|
.address() == txnJson["LimitAmount"]["issuer"]);
|
|
}},
|
|
{6,
|
|
4,
|
|
strHex(txns[txns.size() - 18]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 18]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_set_regular_key()) &&
|
|
BEAST_EXPECT(
|
|
res.set_regular_key()
|
|
.regular_key()
|
|
.value()
|
|
.address() == txnJson["RegularKey"]);
|
|
}},
|
|
{5,
|
|
4,
|
|
strHex(txns[txns.size() - 19]->getTransactionID()),
|
|
[&txns, this](auto res) {
|
|
auto txnJson =
|
|
txns[txns.size() - 19]->getJson(JsonOptions::none);
|
|
return BEAST_EXPECT(res.has_payment()) &&
|
|
BEAST_EXPECT(
|
|
res.payment().amount().value().has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.payment()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == txnJson["Amount"].asUInt()) &&
|
|
BEAST_EXPECT(
|
|
res.payment().destination().value().address() ==
|
|
txnJson["Destination"]) &&
|
|
BEAST_EXPECT(res.has_source_tag()) &&
|
|
BEAST_EXPECT(
|
|
res.source_tag().value() ==
|
|
txnJson["SourceTag"].asUInt()) &&
|
|
BEAST_EXPECT(res.payment().has_destination_tag()) &&
|
|
BEAST_EXPECT(
|
|
res.payment().destination_tag().value() ==
|
|
txnJson["DestinationTag"].asUInt()) &&
|
|
BEAST_EXPECT(res.has_last_ledger_sequence()) &&
|
|
BEAST_EXPECT(
|
|
res.last_ledger_sequence().value() ==
|
|
txnJson["LastLedgerSequence"].asUInt()) &&
|
|
BEAST_EXPECT(res.has_transaction_signature()) &&
|
|
BEAST_EXPECT(res.has_account()) &&
|
|
BEAST_EXPECT(
|
|
res.account().value().address() ==
|
|
txnJson["Account"]) &&
|
|
BEAST_EXPECT(res.has_flags()) &&
|
|
BEAST_EXPECT(
|
|
res.flags().value() == txnJson["Flags"].asUInt());
|
|
}},
|
|
{4,
|
|
4,
|
|
strHex(txns[txns.size() - 20]->getTransactionID()),
|
|
[this](auto res) { return BEAST_EXPECT(res.has_account_set()); }},
|
|
{3,
|
|
3,
|
|
"9CE54C3B934E473A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F",
|
|
[this](auto res) {
|
|
return BEAST_EXPECT(res.has_account_set()) &&
|
|
BEAST_EXPECT(res.account_set().set_flag().value() == 8);
|
|
}},
|
|
{1,
|
|
3,
|
|
"2B5054734FA43C6C7B54F61944FAD6178ACD5D0272B39BA7FCD32A5D3932FBFF",
|
|
[&alice, this](auto res) {
|
|
return BEAST_EXPECT(res.has_payment()) &&
|
|
BEAST_EXPECT(
|
|
res.payment().amount().value().has_xrp_amount()) &&
|
|
BEAST_EXPECT(
|
|
res.payment()
|
|
.amount()
|
|
.value()
|
|
.xrp_amount()
|
|
.drops() == 1000000000010) &&
|
|
BEAST_EXPECT(
|
|
res.payment().destination().value().address() ==
|
|
alice.human());
|
|
}}};
|
|
|
|
using MetaCheck =
|
|
std::function<bool(org::xrpl::rpc::v1::Meta const& res)>;
|
|
static const MetaCheck txMetaCheck[]{
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](org::xrpl::rpc::v1::AffectedNode const&
|
|
entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_CHECK;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_CHECK;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_CHECK;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_CHECK;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_PAY_CHANNEL;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 2) &&
|
|
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_PAY_CHANNEL;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_PAY_CHANNEL;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ESCROW;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ESCROW;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 2) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ESCROW;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ESCROW;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 3) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_SIGNER_LIST;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 4) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_OFFER;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 4) &&
|
|
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_OFFER;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 5) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_DIRECTORY_NODE;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_RIPPLE_STATE;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 2) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 1) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 2) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 1) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 1);
|
|
}},
|
|
{[this](auto meta) {
|
|
return BEAST_EXPECT(meta.transaction_index() == 0) &&
|
|
BEAST_EXPECT(meta.affected_nodes_size() == 2) &&
|
|
BEAST_EXPECT(
|
|
std::count_if(
|
|
meta.affected_nodes().begin(),
|
|
meta.affected_nodes().end(),
|
|
[](auto entry) {
|
|
return entry.ledger_entry_type() ==
|
|
org::xrpl::rpc::v1::LedgerEntryType::
|
|
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT;
|
|
}) == 2);
|
|
}}};
|
|
|
|
auto doCheck = [this](auto txn, auto txCheck) {
|
|
return BEAST_EXPECT(txn.has_transaction()) &&
|
|
BEAST_EXPECT(txn.validated()) &&
|
|
BEAST_EXPECT(strHex(txn.hash()) == txCheck.hash) &&
|
|
BEAST_EXPECT(txn.ledger_index() == txCheck.ledgerIndex) &&
|
|
BEAST_EXPECT(
|
|
txn.transaction().sequence().value() ==
|
|
txCheck.sequence) &&
|
|
txCheck.checkTxn(txn.transaction());
|
|
};
|
|
|
|
auto doMetaCheck = [this](auto txn, auto txMetaCheck) {
|
|
return BEAST_EXPECT(txn.has_meta()) &&
|
|
BEAST_EXPECT(txn.meta().has_transaction_result()) &&
|
|
BEAST_EXPECT(
|
|
txn.meta().transaction_result().result_type() ==
|
|
org::xrpl::rpc::v1::TransactionResult::
|
|
RESULT_TYPE_TES) &&
|
|
BEAST_EXPECT(
|
|
txn.meta().transaction_result().result() ==
|
|
"tesSUCCESS") &&
|
|
txMetaCheck(txn.meta());
|
|
};
|
|
|
|
auto [res, status] = next(grpcPort, env, alice.human());
|
|
|
|
if (!BEAST_EXPECT(status.error_code() == 0))
|
|
return;
|
|
|
|
if (!BEAST_EXPECT(
|
|
res.transactions().size() ==
|
|
std::extent<decltype(txCheck)>::value))
|
|
return;
|
|
for (int i = 0; i < res.transactions().size(); ++i)
|
|
{
|
|
BEAST_EXPECT(doCheck(res.transactions()[i], txCheck[i]));
|
|
BEAST_EXPECT(doMetaCheck(res.transactions()[i], txMetaCheck[i]));
|
|
}
|
|
|
|
// test binary representation
|
|
std::tie(res, status) = nextBinary(grpcPort, env, alice.human());
|
|
|
|
// txns vector does not contain the first two transactions returned by
|
|
// account_tx
|
|
if (!BEAST_EXPECT(res.transactions().size() == txns.size() + 2))
|
|
return;
|
|
|
|
std::reverse(txns.begin(), txns.end());
|
|
for (int i = 0; i < txns.size(); ++i)
|
|
{
|
|
auto toByteString = [](auto data) {
|
|
const char* bytes = reinterpret_cast<const char*>(data.data());
|
|
return std::string(bytes, data.size());
|
|
};
|
|
|
|
auto tx = txns[i];
|
|
Serializer s = tx->getSerializer();
|
|
std::string bin = toByteString(s);
|
|
|
|
BEAST_EXPECT(res.transactions(i).transaction_binary() == bin);
|
|
}
|
|
}
|
|
|
|
void
|
|
testAccountTxPagingGrpc()
|
|
{
|
|
testcase("Test Account_tx Grpc");
|
|
|
|
using namespace test::jtx;
|
|
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
|
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
|
Env env(*this, std::move(config));
|
|
|
|
Account A1{"A1"};
|
|
Account A2{"A2"};
|
|
Account A3{"A3"};
|
|
|
|
env.fund(XRP(10000), A1, A2, A3);
|
|
env.close();
|
|
|
|
env.trust(A3["USD"](1000), A1);
|
|
env.trust(A2["USD"](1000), A1);
|
|
env.trust(A3["USD"](1000), A2);
|
|
env.close();
|
|
|
|
for (auto i = 0; i < 5; ++i)
|
|
{
|
|
env(pay(A2, A1, A2["USD"](2)));
|
|
env(pay(A3, A1, A3["USD"](2)));
|
|
env(offer(A1, XRP(11), A1["USD"](1)));
|
|
env(offer(A2, XRP(10), A2["USD"](1)));
|
|
env(offer(A3, XRP(9), A3["USD"](1)));
|
|
env.close();
|
|
}
|
|
|
|
/* The sequence/ledger for A3 are as follows:
|
|
* seq ledger_index
|
|
* 3 ----> 3
|
|
* 1 ----> 3
|
|
* 2 ----> 4
|
|
* 2 ----> 4
|
|
* 2 ----> 5
|
|
* 3 ----> 5
|
|
* 4 ----> 6
|
|
* 5 ----> 6
|
|
* 6 ----> 7
|
|
* 7 ----> 7
|
|
* 8 ----> 8
|
|
* 9 ----> 8
|
|
* 10 ----> 9
|
|
* 11 ----> 9
|
|
*/
|
|
|
|
// page through the results in several ways.
|
|
{
|
|
// limit = 2, 3 batches giving the first 6 txs
|
|
auto [res, status] = next(grpcPort, env, A3.human(), 2, 5, 2, true);
|
|
|
|
auto txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 3, 3));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 2, 5, 2, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 4));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 2, 5, 2, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 5, 5));
|
|
BEAST_EXPECT(!res.has_marker());
|
|
return;
|
|
}
|
|
|
|
{
|
|
// limit 1, 3 requests giving the first 3 txs
|
|
auto [res, status] = next(grpcPort, env, A3.human(), 3, 9, 1, true);
|
|
auto txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 1, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 1, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
// continue with limit 3, to end of all txs
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 3, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 5, 5));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 3, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 6, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 7, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 8, 7));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 3, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 9, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 10, 8));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 11, 8));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort, env, A3.human(), 3, 9, 3, true, res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 12, 9));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 13, 9));
|
|
BEAST_EXPECT(!res.has_marker());
|
|
}
|
|
|
|
{
|
|
// limit 2, descending, 2 batches giving last 4 txs
|
|
auto [res, status] =
|
|
next(grpcPort, env, A3.human(), 3, 9, 2, false);
|
|
auto txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 13, 9));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 12, 9));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort,
|
|
env,
|
|
A3.human(),
|
|
3,
|
|
9,
|
|
2,
|
|
false,
|
|
res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 2))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 11, 8));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 10, 8));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
// continue with limit 3 until all txs have been seen
|
|
std::tie(res, status) = next(
|
|
grpcPort,
|
|
env,
|
|
A3.human(),
|
|
3,
|
|
9,
|
|
3,
|
|
false,
|
|
res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 9, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 8, 7));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 7, 6));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort,
|
|
env,
|
|
A3.human(),
|
|
3,
|
|
9,
|
|
3,
|
|
false,
|
|
res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 6, 6));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 5, 5));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 4, 5));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort,
|
|
env,
|
|
A3.human(),
|
|
3,
|
|
9,
|
|
3,
|
|
false,
|
|
res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 3))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[1u], 4, 4));
|
|
BEAST_EXPECT(checkTransaction(txs[2u], 3, 3));
|
|
if (!BEAST_EXPECT(res.has_marker()))
|
|
return;
|
|
|
|
std::tie(res, status) = next(
|
|
grpcPort,
|
|
env,
|
|
A3.human(),
|
|
3,
|
|
9,
|
|
3,
|
|
false,
|
|
res.mutable_marker());
|
|
txs = res.transactions();
|
|
if (!BEAST_EXPECT(txs.size() == 1))
|
|
return;
|
|
BEAST_EXPECT(checkTransaction(txs[0u], 3, 3));
|
|
BEAST_EXPECT(!res.has_marker());
|
|
}
|
|
}
|
|
|
|
public:
|
|
void
|
|
run() override
|
|
{
|
|
testAccountTxPaging();
|
|
testAccountTxPagingGrpc();
|
|
testAccountTxParametersGrpc();
|
|
testAccountTxContentsGrpc();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple);
|
|
|
|
} // namespace ripple
|