gRPC support for account_tx and tx

- Add support for all transaction types and ledger object types to gRPC
  implementation of tx and account_tx.

- Create common handlers for tx and account_tx.

- Remove mutex and abort() from gRPC server. JobQueue is stopped before
  gRPC server, with all coroutines executed to completion, so no need for
  synchronization.
This commit is contained in:
CJ Cobb
2020-02-04 12:31:17 -08:00
committed by Mike Ellery
parent acf4b78892
commit e7ce3909d2
57 changed files with 7498 additions and 2106 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -82,25 +82,6 @@ public:
}
};
/** Set InvoiceID on a JTx. */
class invoice_id
{
private:
uint256 const id_;
public:
explicit invoice_id (uint256 const& id)
: id_{id}
{
}
void
operator()(Env&, JTx& jt) const
{
jt[sfInvoiceID.jsonName] = to_string (id_);
}
};
} // namespace jtx
} // namespace test

View File

@@ -24,6 +24,7 @@
#include <ripple/json/to_string.h>
#include <test/jtx/Account.h>
#include <test/jtx/account_txn_id.h>
#include <test/jtx/acctdelete.h>
#include <test/jtx/amount.h>
#include <test/jtx/balance.h>
@@ -34,8 +35,10 @@
#include <test/jtx/Env_ss.h>
#include <test/jtx/fee.h>
#include <test/jtx/flags.h>
#include <test/jtx/invoice_id.h>
#include <test/jtx/jtx_json.h>
#include <test/jtx/JTx.h>
#include <test/jtx/last_ledger_sequence.h>
#include <test/jtx/memo.h>
#include <test/jtx/multisign.h>
#include <test/jtx/noop.h>

View File

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

View File

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

View File

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

View File

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

42
src/test/jtx/invoice_id.h Normal file
View File

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

View File

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

View File

@@ -324,8 +324,8 @@ public:
class GetAccountInfoClient : public GRPCTestClientBase
{
public:
rpc::v1::GetAccountInfoRequest request;
rpc::v1::GetAccountInfoResponse reply;
org::xrpl::rpc::v1::GetAccountInfoRequest request;
org::xrpl::rpc::v1::GetAccountInfoResponse reply;
explicit GetAccountInfoClient(std::string const& port)
: GRPCTestClientBase(port)
@@ -358,11 +358,10 @@ public:
client.GetAccountInfo();
if (!BEAST_EXPECT(client.status.ok()))
{
std::cout << client.reply.DebugString() << std::endl;
return;
}
BEAST_EXPECT(
client.reply.account_data().account().address() ==
client.reply.account_data().account().value().address() ==
alice.human());
}
{
@@ -374,13 +373,13 @@ public:
if (!BEAST_EXPECT(client.status.ok()))
return;
BEAST_EXPECT(
client.reply.account_data().balance().drops() ==
client.reply.account_data().balance().value().xrp_amount().drops() ==
1000 * 1000 * 1000);
BEAST_EXPECT(
client.reply.account_data().account().address() ==
client.reply.account_data().account().value().address() ==
alice.human());
BEAST_EXPECT(
client.reply.account_data().sequence() == env.seq(alice));
client.reply.account_data().sequence().value() == env.seq(alice));
BEAST_EXPECT(client.reply.queue_data().txn_count() == 0);
}
}
@@ -473,7 +472,7 @@ public:
{
return;
}
BEAST_EXPECT(client.reply.account_data().owner_count() == 1);
BEAST_EXPECT(client.reply.account_data().owner_count().value() == 1);
BEAST_EXPECT(client.reply.signer_list().signer_entries_size() == 1);
}
@@ -518,16 +517,16 @@ public:
{
return;
}
BEAST_EXPECT(client.reply.account_data().owner_count() == 1);
BEAST_EXPECT(client.reply.account_data().owner_count().value() == 1);
auto& signerList = client.reply.signer_list();
BEAST_EXPECT(signerList.signer_quorum() == 4);
BEAST_EXPECT(signerList.signer_quorum().value() == 4);
BEAST_EXPECT(signerList.signer_entries_size() == 8);
for (int i = 0; i < 8; ++i)
{
BEAST_EXPECT(signerList.signer_entries(i).signer_weight() == 1);
BEAST_EXPECT(signerList.signer_entries(i).signer_weight().value() == 1);
BEAST_EXPECT(
accounts.erase(
signerList.signer_entries(i).account().address()) == 1);
signerList.signer_entries(i).account().value().address()) == 1);
}
BEAST_EXPECT(accounts.size() == 0);
}

View File

@@ -23,13 +23,13 @@
#include <ripple/core/DatabaseCon.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/jss.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <test/rpc/GRPCTestClientBase.h>
#include <test/jtx.h>
#include <test/jtx/Env.h>
#include <test/jtx/envconfig.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <test/rpc/GRPCTestClientBase.h>
namespace ripple {
namespace test {
@@ -39,8 +39,8 @@ class Fee_test : public beast::unit_test::suite
class GrpcFeeClient : public GRPCTestClientBase
{
public:
rpc::v1::GetFeeRequest request;
rpc::v1::GetFeeResponse reply;
org::xrpl::rpc::v1::GetFeeRequest request;
org::xrpl::rpc::v1::GetFeeResponse reply;
explicit GrpcFeeClient(std::string const& grpcPort)
: GRPCTestClientBase(grpcPort)
@@ -54,12 +54,12 @@ class Fee_test : public beast::unit_test::suite
}
};
std::pair<bool, rpc::v1::GetFeeResponse>
std::pair<bool, org::xrpl::rpc::v1::GetFeeResponse>
grpcGetFee(std::string const& grpcPort)
{
GrpcFeeClient client(grpcPort);
client.GetFee();
return std::pair<bool, rpc::v1::GetFeeResponse>(
return std::pair<bool, org::xrpl::rpc::v1::GetFeeResponse>(
client.status.ok(), client.reply);
}
@@ -104,29 +104,29 @@ class Fee_test : public beast::unit_test::suite
BEAST_EXPECT(reply.max_queue_size() == *metrics.txQMaxSize);
// fee levels data
rpc::v1::FeeLevels& levels = *reply.mutable_levels();
org::xrpl::rpc::v1::FeeLevels& levels = *reply.mutable_levels();
BEAST_EXPECT(levels.median_level() == metrics.medFeeLevel);
BEAST_EXPECT(levels.minimum_level() == metrics.minProcessingFeeLevel);
BEAST_EXPECT(levels.open_ledger_level() == metrics.openLedgerFeeLevel);
BEAST_EXPECT(levels.reference_level() == metrics.referenceFeeLevel);
// fee data
rpc::v1::Fee& drops = *reply.mutable_drops();
org::xrpl::rpc::v1::Fee& fee = *reply.mutable_fee();
auto const baseFee = view->fees().base;
BEAST_EXPECT(
drops.base_fee().drops() ==
fee.base_fee().drops() ==
toDrops(metrics.referenceFeeLevel, baseFee).second);
BEAST_EXPECT(
drops.minimum_fee().drops() ==
fee.minimum_fee().drops() ==
toDrops(metrics.minProcessingFeeLevel, baseFee).second);
BEAST_EXPECT(
drops.median_fee().drops() ==
fee.median_fee().drops() ==
toDrops(metrics.medFeeLevel, baseFee).second);
auto openLedgerFee =
toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee)
.second +
1;
BEAST_EXPECT(drops.open_ledger_fee().drops() == openLedgerFee.drops());
BEAST_EXPECT(fee.open_ledger_fee().drops() == openLedgerFee.drops());
}
public:

View File

@@ -20,7 +20,7 @@
#ifndef RIPPLED_GRPCTESTCLIENTBASE_H
#define RIPPLED_GRPCTESTCLIENTBASE_H
#include <rpc/v1/xrp_ledger.grpc.pb.h>
#include <org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h>
#include <test/jtx/envconfig.h>
namespace ripple {
@@ -29,7 +29,7 @@ namespace test {
struct GRPCTestClientBase
{
explicit GRPCTestClientBase(std::string const& port)
: stub_(rpc::v1::XRPLedgerAPIService::NewStub(grpc::CreateChannel(
: stub_(org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(grpc::CreateChannel(
beast::IP::Endpoint(
boost::asio::ip::make_address(getEnvLocalhostAddr()),
std::stoi(port))
@@ -40,7 +40,7 @@ struct GRPCTestClientBase
grpc::Status status;
grpc::ClientContext context;
std::unique_ptr<rpc::v1::XRPLedgerAPIService::Stub> stub_;
std::unique_ptr<org::xrpl::rpc::v1::XRPLedgerAPIService::Stub> stub_;
};
} // namespace test

View File

@@ -36,8 +36,8 @@ public:
class SubmitClient : public GRPCTestClientBase
{
public:
rpc::v1::SubmitTransactionRequest request;
rpc::v1::SubmitTransactionResponse reply;
org::xrpl::rpc::v1::SubmitTransactionRequest request;
org::xrpl::rpc::v1::SubmitTransactionResponse reply;
explicit SubmitClient(std::string const& port)
: GRPCTestClientBase(port)

View File

@@ -29,9 +29,12 @@
#include <test/jtx/envconfig.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/GRPCHelpers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <test/rpc/GRPCTestClientBase.h>
#include <string>
namespace ripple {
namespace test {
@@ -46,16 +49,23 @@ class Tx_test : public beast::unit_test::suite
}
void
cmpAmount(const rpc::v1::CurrencyAmount& proto_amount, STAmount amount)
cmpAmount(
const org::xrpl::rpc::v1::CurrencyAmount& proto_amount,
STAmount amount)
{
if (amount.native())
{
if (!BEAST_EXPECT(proto_amount.has_xrp_amount()))
return;
BEAST_EXPECT(
proto_amount.xrp_amount().drops() == amount.xrp().drops());
}
else
{
rpc::v1::IssuedCurrencyAmount issuedCurrency =
if (!BEAST_EXPECT(proto_amount.has_issued_currency_amount()))
return;
org::xrpl::rpc::v1::IssuedCurrencyAmount issuedCurrency =
proto_amount.issued_currency_amount();
Issue const& issue = amount.issue();
Currency currency = issue.currency;
@@ -70,60 +80,183 @@ class Tx_test : public beast::unit_test::suite
}
void
cmpTx(const rpc::v1::Transaction& proto, std::shared_ptr<STTx const> txnSt)
cmpPaymentTx(
const org::xrpl::rpc::v1::Transaction& proto,
std::shared_ptr<STTx const> txnSt)
{
if (!BEAST_EXPECT(proto.has_payment()))
return;
if (!BEAST_EXPECT(
safe_cast<TxType>(txnSt->getFieldU16(sfTransactionType)) ==
TxType::ttPAYMENT))
return;
AccountID account = txnSt->getAccountID(sfAccount);
BEAST_EXPECT(proto.account().address() == toBase58(account));
if (!BEAST_EXPECT(proto.has_account()))
return;
BEAST_EXPECT(proto.account().value().address() == toBase58(account));
STAmount amount = txnSt->getFieldAmount(sfAmount);
cmpAmount(proto.payment().amount(), amount);
if (!BEAST_EXPECT(proto.payment().has_amount()))
return;
cmpAmount(proto.payment().amount().value(), amount);
AccountID accountDest = txnSt->getAccountID(sfDestination);
if (!BEAST_EXPECT(proto.payment().has_destination()))
return;
BEAST_EXPECT(
proto.payment().destination().address() == toBase58(accountDest));
proto.payment().destination().value().address() ==
toBase58(accountDest));
STAmount fee = txnSt->getFieldAmount(sfFee);
if (!BEAST_EXPECT(proto.has_fee()))
return;
BEAST_EXPECT(proto.fee().drops() == fee.xrp().drops());
BEAST_EXPECT(proto.sequence() == txnSt->getFieldU32(sfSequence));
if (!BEAST_EXPECT(proto.has_sequence()))
return;
BEAST_EXPECT(
proto.sequence().value() == txnSt->getFieldU32(sfSequence));
if (!BEAST_EXPECT(proto.has_signing_public_key()))
return;
Blob signingPubKey = txnSt->getFieldVL(sfSigningPubKey);
BEAST_EXPECT(proto.signing_public_key() == toByteString(signingPubKey));
BEAST_EXPECT(proto.flags() == txnSt->getFieldU32(sfFlags));
BEAST_EXPECT(
proto.last_ledger_sequence() ==
txnSt->getFieldU32(sfLastLedgerSequence));
proto.signing_public_key().value() == toByteString(signingPubKey));
Blob blob = txnSt->getFieldVL(sfTxnSignature);
BEAST_EXPECT(proto.signature() == toByteString(blob));
if (txnSt->isFieldPresent(sfFlags))
{
if (!BEAST_EXPECT(proto.has_flags()))
return;
BEAST_EXPECT(proto.flags().value() == txnSt->getFieldU32(sfFlags));
}
else
{
BEAST_EXPECT(!proto.has_flags());
}
if (txnSt->isFieldPresent(sfLastLedgerSequence))
{
if (!BEAST_EXPECT(proto.has_last_ledger_sequence()))
return;
BEAST_EXPECT(
proto.last_ledger_sequence().value() ==
txnSt->getFieldU32(sfLastLedgerSequence));
}
else
{
BEAST_EXPECT(!proto.has_last_ledger_sequence());
}
if (txnSt->isFieldPresent(sfTxnSignature))
{
if (!BEAST_EXPECT(proto.has_transaction_signature()))
return;
Blob blob = txnSt->getFieldVL(sfTxnSignature);
BEAST_EXPECT(
proto.transaction_signature().value() == toByteString(blob));
}
if (txnSt->isFieldPresent(sfSendMax))
{
if (!BEAST_EXPECT(proto.payment().has_send_max()))
return;
STAmount const& send_max = txnSt->getFieldAmount(sfSendMax);
cmpAmount(proto.payment().send_max(), send_max);
cmpAmount(proto.payment().send_max().value(), send_max);
}
else
{
BEAST_EXPECT(!proto.payment().has_send_max());
}
if (txnSt->isFieldPresent(sfAccountTxnID))
{
if (!BEAST_EXPECT(proto.has_account_transaction_id()))
return;
auto field = txnSt->getFieldH256(sfAccountTxnID);
BEAST_EXPECT(proto.account_transaction_id() == toByteString(field));
BEAST_EXPECT(
proto.account_transaction_id().value() == toByteString(field));
}
else
{
BEAST_EXPECT(!proto.has_account_transaction_id());
}
if (txnSt->isFieldPresent(sfSourceTag))
{
if (!BEAST_EXPECT(proto.has_source_tag()))
return;
BEAST_EXPECT(
proto.source_tag().value() == txnSt->getFieldU32(sfSourceTag));
}
else
{
BEAST_EXPECT(!proto.has_source_tag());
}
if (txnSt->isFieldPresent(sfDestinationTag))
{
if (!BEAST_EXPECT(proto.payment().has_destination_tag()))
return;
BEAST_EXPECT(
proto.payment().destination_tag().value() ==
txnSt->getFieldU32(sfDestinationTag));
}
else
{
BEAST_EXPECT(!proto.payment().has_destination_tag());
}
if (txnSt->isFieldPresent(sfInvoiceID))
{
if (!BEAST_EXPECT(proto.payment().has_invoice_id()))
return;
auto field = txnSt->getFieldH256(sfInvoiceID);
BEAST_EXPECT(
proto.payment().invoice_id().value() == toByteString(field));
}
else
{
BEAST_EXPECT(!proto.payment().has_invoice_id());
}
if (txnSt->isFieldPresent(sfDeliverMin))
{
if (!BEAST_EXPECT(proto.payment().has_deliver_min()))
return;
STAmount const& deliverMin = txnSt->getFieldAmount(sfDeliverMin);
cmpAmount(proto.payment().deliver_min().value(), deliverMin);
}
else
{
BEAST_EXPECT(!proto.payment().has_deliver_min());
}
// populate path data
STPathSet const& pathset = txnSt->getFieldPathSet(sfPaths);
if (!BEAST_EXPECT(pathset.size() == proto.payment().paths_size()))
return;
int ind = 0;
for (auto it = pathset.begin(); it < pathset.end(); ++it)
{
STPath const& path = *it;
const rpc::v1::Path& protoPath = proto.payment().paths(ind++);
const org::xrpl::rpc::v1::Payment_Path& protoPath =
proto.payment().paths(ind++);
if (!BEAST_EXPECT(protoPath.elements_size() == path.size()))
continue;
int ind2 = 0;
for (auto it2 = path.begin(); it2 != path.end(); ++it2)
{
const rpc::v1::PathElement& protoElement =
const org::xrpl::rpc::v1::Payment_PathElement& protoElement =
protoPath.elements(ind2++);
STPathElement const& elt = *it2;
@@ -132,47 +265,224 @@ class Tx_test : public beast::unit_test::suite
if (elt.hasCurrency())
{
Currency const& currency = elt.getCurrency();
BEAST_EXPECT(
protoElement.currency().name() ==
to_string(currency));
if (BEAST_EXPECT(protoElement.has_currency()))
{
BEAST_EXPECT(
protoElement.currency().name() ==
to_string(currency));
}
}
else
{
BEAST_EXPECT(!protoElement.has_currency());
}
if (elt.hasIssuer())
{
AccountID const& issuer = elt.getIssuerID();
BEAST_EXPECT(
protoElement.issuer().address() ==
toBase58(issuer));
if (BEAST_EXPECT(protoElement.has_issuer()))
{
BEAST_EXPECT(
protoElement.issuer().address() ==
toBase58(issuer));
}
}
else
{
BEAST_EXPECT(!protoElement.has_issuer());
}
}
else
{
AccountID const& path_account = elt.getAccountID();
BEAST_EXPECT(
protoElement.account().address() ==
toBase58(path_account));
if (BEAST_EXPECT(protoElement.has_account()))
{
AccountID const& path_account = elt.getAccountID();
BEAST_EXPECT(
protoElement.account().address() ==
toBase58(path_account));
}
else
{
BEAST_EXPECT(!protoElement.has_account());
}
BEAST_EXPECT(!protoElement.has_issuer());
BEAST_EXPECT(!protoElement.has_currency());
}
}
}
if (txnSt->isFieldPresent(sfMemos))
{
auto arr = txnSt->getFieldArray(sfMemos);
if (BEAST_EXPECT(proto.memos_size() == arr.size()))
{
for (size_t i = 0; i < arr.size(); ++i)
{
auto protoMemo = proto.memos(i);
auto stMemo = arr[i];
if (stMemo.isFieldPresent(sfMemoData))
{
if (BEAST_EXPECT(protoMemo.has_memo_data()))
{
BEAST_EXPECT(
protoMemo.memo_data().value() ==
toByteString(stMemo.getFieldVL(sfMemoData)));
}
}
else
{
BEAST_EXPECT(!protoMemo.has_memo_data());
}
if (stMemo.isFieldPresent(sfMemoType))
{
if (BEAST_EXPECT(protoMemo.has_memo_type()))
{
BEAST_EXPECT(
protoMemo.memo_type().value() ==
toByteString(stMemo.getFieldVL(sfMemoType)));
}
}
else
{
BEAST_EXPECT(!protoMemo.has_memo_type());
}
if (stMemo.isFieldPresent(sfMemoFormat))
{
if (BEAST_EXPECT(protoMemo.has_memo_format()))
{
BEAST_EXPECT(
protoMemo.memo_format().value() ==
toByteString(stMemo.getFieldVL(sfMemoFormat)));
}
}
else
{
BEAST_EXPECT(!protoMemo.has_memo_format());
}
}
}
}
else
{
BEAST_EXPECT(proto.memos_size() == 0);
}
if (txnSt->isFieldPresent(sfSigners))
{
auto arr = txnSt->getFieldArray(sfSigners);
if (BEAST_EXPECT(proto.signers_size() == arr.size()))
{
for (size_t i = 0; i < arr.size(); ++i)
{
auto protoSigner = proto.signers(i);
auto stSigner = arr[i];
if (stSigner.isFieldPresent(sfAccount))
{
if (BEAST_EXPECT(protoSigner.has_account()))
{
BEAST_EXPECT(
protoSigner.account().value().address() ==
toBase58(stSigner.getAccountID(sfAccount)));
}
}
else
{
BEAST_EXPECT(!protoSigner.has_account());
}
if (stSigner.isFieldPresent(sfTxnSignature))
{
if (BEAST_EXPECT(
protoSigner.has_transaction_signature()))
{
Blob blob = stSigner.getFieldVL(sfTxnSignature);
BEAST_EXPECT(
protoSigner.transaction_signature().value() ==
toByteString(blob));
}
}
else
{
BEAST_EXPECT(!protoSigner.has_transaction_signature());
}
if (stSigner.isFieldPresent(sfSigningPubKey))
{
if (BEAST_EXPECT(protoSigner.has_signing_public_key()))
{
Blob signingPubKey =
stSigner.getFieldVL(sfSigningPubKey);
BEAST_EXPECT(
protoSigner.signing_public_key().value() ==
toByteString(signingPubKey));
}
}
else
{
BEAST_EXPECT(!protoSigner.has_signing_public_key());
}
}
}
}
else
{
BEAST_EXPECT(proto.signers_size() == 0);
}
}
void
cmpMeta(const rpc::v1::Meta& proto, std::shared_ptr<TxMeta> txMeta)
cmpMeta(
const org::xrpl::rpc::v1::Meta& proto,
std::shared_ptr<TxMeta> txMeta)
{
BEAST_EXPECT(proto.transaction_index() == txMeta->getIndex());
BEAST_EXPECT(
proto.transaction_result().result() ==
transToken(txMeta->getResultTER()));
rpc::v1::TransactionResult r;
org::xrpl::rpc::v1::TransactionResult r;
RPC::populateTransactionResultType(r, txMeta->getResultTER());
RPC::convert(r, txMeta->getResultTER());
BEAST_EXPECT(
proto.transaction_result().result_type() == r.result_type());
}
if (txMeta->hasDeliveredAmount())
void
cmpDeliveredAmount(
const org::xrpl::rpc::v1::Meta& meta,
const org::xrpl::rpc::v1::Transaction& txn,
const std::shared_ptr<TxMeta> expMeta,
const std::shared_ptr<STTx const> expTxn,
bool checkAmount = true)
{
if (expMeta->hasDeliveredAmount())
{
cmpAmount(proto.delivered_amount(), txMeta->getDeliveredAmount());
if (!BEAST_EXPECT(meta.has_delivered_amount()))
return;
cmpAmount(
meta.delivered_amount().value(), expMeta->getDeliveredAmount());
}
else
{
if (expTxn->isFieldPresent(sfAmount))
{
using namespace std::chrono_literals;
if (checkAmount)
{
cmpAmount(
meta.delivered_amount().value(),
expTxn->getFieldAmount(sfAmount));
}
}
else
{
BEAST_EXPECT(!meta.has_delivered_amount());
}
}
}
@@ -180,8 +490,8 @@ class Tx_test : public beast::unit_test::suite
class GrpcTxClient : public GRPCTestClientBase
{
public:
rpc::v1::GetTxRequest request;
rpc::v1::GetTxResponse reply;
org::xrpl::rpc::v1::GetTransactionRequest request;
org::xrpl::rpc::v1::GetTransactionResponse reply;
explicit GrpcTxClient(std::string const& port)
: GRPCTestClientBase(port)
@@ -191,7 +501,7 @@ class Tx_test : public beast::unit_test::suite
void
Tx()
{
status = stub_->GetTx(&context, request, &reply);
status = stub_->GetTransaction(&context, request, &reply);
}
};
@@ -205,33 +515,137 @@ class Tx_test : public beast::unit_test::suite
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
Env env(*this, std::move(config));
using namespace std::chrono_literals;
// Set time to this value (or greater) to get delivered_amount in meta
env.timeKeeper().set(NetClock::time_point{446000001s});
auto grpcTx = [&grpcPort](auto hash, auto binary) {
GrpcTxClient client(grpcPort);
client.request.set_hash(&hash, sizeof(hash));
client.request.set_binary(binary);
client.Tx();
return std::pair<bool, rpc::v1::GetTxResponse>(
return std::pair<bool, org::xrpl::rpc::v1::GetTransactionResponse>(
client.status.ok(), client.reply);
};
Account A1{"A1"};
Account A2{"A2"};
Account A3{"A3"};
env.fund(XRP(10000), A1);
env.fund(XRP(10000), A2);
env.close();
env.trust(A2["USD"](1000), A1);
env.close();
env(fset(A2, 5)); // set asfAccountTxnID flag
// SignerListSet
env(signers(A2, 1, {{"bogie", 1}, {"demon", 1}, {A1, 1}, {A3, 1}}),
sig(A2));
env.close();
std::vector<std::shared_ptr<STTx const>> txns;
auto const startLegSeq = env.current()->info().seq;
uint256 prevHash;
for (int i = 0; i < 14; ++i)
{
auto const baseFee = env.current()->fees().base;
auto txfee = fee(i + (2 * baseFee));
auto lls = last_ledger_seq(i + startLegSeq + 20);
auto dsttag = dtag(i * 456);
auto srctag = stag(i * 321);
auto sm = sendmax(A2["USD"](1000));
auto dm = delivermin(A2["USD"](50));
auto txf = txflags(131072); // partial payment flag
auto txnid = account_txn_id(prevHash);
auto inv = invoice_id(prevHash);
auto mem1 = memo("foo", "bar", "baz");
auto mem2 = memo("dragons", "elves", "goblins");
if (i & 1)
env(pay(A2, A1, A2["USD"](100)));
{
if (i & 2)
{
env(pay(A2, A1, A2["USD"](100)),
txfee,
srctag,
dsttag,
lls,
sm,
dm,
txf,
txnid,
inv,
mem1,
mem2,
sig(A2));
}
else
{
env(pay(A2, A1, A2["USD"](100)),
txfee,
srctag,
dsttag,
lls,
sm,
dm,
txf,
txnid,
inv,
mem1,
mem2,
msig(A3));
}
}
else
env(pay(A2, A1, A2["XRP"](200)));
{
if (i & 2)
{
env(pay(A2, A1, A2["XRP"](200)),
txfee,
srctag,
dsttag,
lls,
txnid,
inv,
mem1,
mem2,
sig(A2));
}
else
{
env(pay(A2, A1, A2["XRP"](200)),
txfee,
srctag,
dsttag,
lls,
txnid,
inv,
mem1,
mem2,
msig(A3));
}
}
txns.emplace_back(env.tx());
prevHash = txns.back()->getTransactionID();
env.close();
}
// Payment with Paths
auto const gw = Account("gateway");
auto const USD = gw["USD"];
env.fund(XRP(10000), "alice", "bob", gw);
env.trust(USD(600), "alice");
env.trust(USD(700), "bob");
env(pay(gw, "alice", USD(70)));
txns.emplace_back(env.tx());
env.close();
env(pay(gw, "bob", USD(50)));
txns.emplace_back(env.tx());
env.close();
env(pay("alice", "bob", Account("bob")["USD"](5)), path(gw));
txns.emplace_back(env.tx());
env.close();
auto const endLegSeq = env.closed()->info().seq;
// Find the existing transactions
@@ -257,7 +671,7 @@ class Tx_test : public beast::unit_test::suite
}
else
{
cmpTx(result.second.transaction(), tx);
cmpPaymentTx(result.second.transaction(), tx);
}
if (ledger && !b)
@@ -269,6 +683,11 @@ class Tx_test : public beast::unit_test::suite
id, ledger->seq(), *rawMeta);
cmpMeta(result.second.meta(), txMeta);
cmpDeliveredAmount(
result.second.meta(),
result.second.transaction(),
txMeta,
tx);
}
}
}
@@ -299,6 +718,41 @@ class Tx_test : public beast::unit_test::suite
BEAST_EXPECT(result.first == false);
}
// non final transaction
env(pay(A2, A1, A2["XRP"](200)));
auto res = grpcTx(env.tx()->getTransactionID(), false);
BEAST_EXPECT(res.first);
BEAST_EXPECT(res.second.has_transaction());
if (!BEAST_EXPECT(res.second.has_meta()))
return;
if (!BEAST_EXPECT(res.second.meta().has_transaction_result()))
return;
BEAST_EXPECT(
res.second.meta().transaction_result().result() == "tesSUCCESS");
BEAST_EXPECT(
res.second.meta().transaction_result().result_type() ==
org::xrpl::rpc::v1::TransactionResult::RESULT_TYPE_TES);
BEAST_EXPECT(!res.second.validated());
BEAST_EXPECT(!res.second.meta().has_delivered_amount());
env.close();
res = grpcTx(env.tx()->getTransactionID(), false);
BEAST_EXPECT(res.first);
BEAST_EXPECT(res.second.has_transaction());
if (!BEAST_EXPECT(res.second.has_meta()))
return;
if (!BEAST_EXPECT(res.second.meta().has_transaction_result()))
return;
BEAST_EXPECT(
res.second.meta().transaction_result().result() == "tesSUCCESS");
BEAST_EXPECT(
res.second.meta().transaction_result().result_type() ==
org::xrpl::rpc::v1::TransactionResult::RESULT_TYPE_TES);
BEAST_EXPECT(res.second.validated());
BEAST_EXPECT(res.second.meta().has_delivered_amount());
}
public: