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

View File

@@ -3,6 +3,7 @@
core functionality, useable by some client software perhaps
#]===================================================================]
file (GLOB_RECURSE rb_headers
src/ripple/beast/*.h
src/ripple/beast/*.hpp)
@@ -613,6 +614,7 @@ target_sources (rippled PRIVATE
src/ripple/rpc/handlers/WalletPropose.cpp
src/ripple/rpc/impl/DeliveredAmount.cpp
src/ripple/rpc/impl/Handler.cpp
src/ripple/rpc/impl/GRPCHelpers.cpp
src/ripple/rpc/impl/LegacyPathFind.cpp
src/ripple/rpc/impl/RPCHandler.cpp
src/ripple/rpc/impl/RPCHelpers.cpp
@@ -621,6 +623,7 @@ target_sources (rippled PRIVATE
src/ripple/rpc/impl/ShardArchiveHandler.cpp
src/ripple/rpc/impl/Status.cpp
src/ripple/rpc/impl/TransactionSign.cpp
#[===============================[
main sources:
subdir: server
@@ -786,6 +789,7 @@ target_sources (rippled PRIVATE
src/test/jtx/impl/ManualTimeKeeper.cpp
src/test/jtx/impl/WSClient.cpp
src/test/jtx/impl/acctdelete.cpp
src/test/jtx/impl/account_txn_id.cpp
src/test/jtx/impl/amount.cpp
src/test/jtx/impl/balance.cpp
src/test/jtx/impl/check.cpp
@@ -794,7 +798,9 @@ target_sources (rippled PRIVATE
src/test/jtx/impl/envconfig.cpp
src/test/jtx/impl/fee.cpp
src/test/jtx/impl/flags.cpp
src/test/jtx/impl/invoice_id.cpp
src/test/jtx/impl/jtx_json.cpp
src/test/jtx/impl/last_ledger_sequence.cpp
src/test/jtx/impl/memo.cpp
src/test/jtx/impl/multisign.cpp
src/test/jtx/impl/offer.cpp
@@ -812,6 +818,7 @@ target_sources (rippled PRIVATE
src/test/jtx/impl/trust.cpp
src/test/jtx/impl/txflags.cpp
src/test/jtx/impl/utility.cpp
#[===============================[
test sources:
subdir: ledger

View File

@@ -309,7 +309,7 @@ set (GRPC_GEN_DIR "${CMAKE_BINARY_DIR}/proto_gen_grpc")
file (MAKE_DIRECTORY ${GRPC_GEN_DIR})
set (GRPC_PROTO_SRCS)
set (GRPC_PROTO_HDRS)
set (GRPC_PROTO_ROOT "${CMAKE_SOURCE_DIR}/src/ripple/proto/rpc")
set (GRPC_PROTO_ROOT "${CMAKE_SOURCE_DIR}/src/ripple/proto/org")
file(GLOB_RECURSE GRPC_DEFINITION_FILES LIST_DIRECTORIES false "${GRPC_PROTO_ROOT}/*.proto")
foreach(file ${GRPC_DEFINITION_FILES})
get_filename_component(_abs_file ${file} ABSOLUTE)

View File

@@ -41,7 +41,7 @@ getEndpoint(std::string const& peer)
template <class Request, class Response>
GRPCServerImpl::CallData<Request, Response>::CallData(
rpc::v1::XRPLedgerAPIService::AsyncService& service,
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService& service,
grpc::ServerCompletionQueue& cq,
Application& app,
BindListener<Request, Response> bindListener,
@@ -52,7 +52,6 @@ GRPCServerImpl::CallData<Request, Response>::CallData(
, cq_(cq)
, finished_(false)
, app_(app)
, aborted_(false)
, responder_(&ctx_)
, bindListener_(std::move(bindListener))
, handler_(std::move(handler))
@@ -87,20 +86,31 @@ GRPCServerImpl::CallData<Request, Response>::process()
std::shared_ptr<CallData<Request, Response>> thisShared =
this->shared_from_this();
app_.getJobQueue().postCoro(
// Need to set finished to true before processing the response,
// because as soon as the response is posted to the completion
// queue (via responder_.Finish(...) or responder_.FinishWithError(...)),
// the CallData object is returned as a tag in handleRpcs().
// handleRpcs() checks the finished variable, and if true, destroys
// the object. Setting finished to true before calling process
// ensures that finished is always true when this CallData object
// is returned as a tag in handleRpcs(), after sending the response
finished_ = true;
auto coro = app_.getJobQueue().postCoro(
JobType::jtRPC,
"gRPC-Client",
[thisShared](std::shared_ptr<JobQueue::Coro> coro) {
std::lock_guard lock{thisShared->mut_};
// Do nothing if call has been aborted due to server shutdown
// or if handler was already executed
if (thisShared->aborted_ || thisShared->finished_)
return;
thisShared->process(coro);
thisShared->finished_ = true;
});
// If coro is null, then the JobQueue has already been shutdown
if (!coro)
{
grpc::Status status{grpc::StatusCode::INTERNAL,
"Job Queue is already stopped"};
responder_.FinishWithError(status, this);
}
}
template <class Request, class Response>
@@ -141,7 +151,7 @@ GRPCServerImpl::CallData<Request, Response>::process(
if (conditionMetRes != rpcSUCCESS)
{
RPC::ErrorInfo errorInfo = RPC::get_error_info(conditionMetRes);
grpc::Status status{grpc::StatusCode::INTERNAL,
grpc::Status status{grpc::StatusCode::FAILED_PRECONDITION,
errorInfo.message.c_str()};
responder_.FinishWithError(status, this);
}
@@ -163,21 +173,9 @@ template <class Request, class Response>
bool
GRPCServerImpl::CallData<Request, Response>::isFinished()
{
// Need to lock here because this object can be returned from cq_.Next(..)
// as soon as the response is sent, which could be before finished_ is set
// to true, causing the handler to be executed twice
std::lock_guard lock{mut_};
return finished_;
}
template <class Request, class Response>
void
GRPCServerImpl::CallData<Request, Response>::abort()
{
std::lock_guard lock{mut_};
aborted_ = true;
}
template <class Request, class Response>
Resource::Charge
GRPCServerImpl::CallData<Request, Response>::getLoadType()
@@ -202,7 +200,8 @@ GRPCServerImpl::CallData<Request, Response>::getUsage()
return app_.getResourceManager().newInboundEndpoint(endpoint.get());
}
GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app)
GRPCServerImpl::GRPCServerImpl(Application& app)
: app_(app), journal_(app_.journal("gRPC Server"))
{
// if present, get endpoint from config
if (app_.config().exists("port_grpc"))
@@ -233,9 +232,24 @@ GRPCServerImpl::GRPCServerImpl(Application& app) : app_(app)
void
GRPCServerImpl::shutdown()
{
JLOG(journal_.debug()) << "Shutting down";
//The below call cancels all "listeners" (CallData objects that are waiting
//for a request, as opposed to processing a request), and blocks until all
//requests being processed are completed. CallData objects in the midst of
//processing requests need to actually send data back to the client, via
//responder_.Finish(...) or responder_.FinishWithError(...), for this call
//to unblock. Each cancelled listener is returned via cq_.Next(...) with ok
//set to false
server_->Shutdown();
// Always shutdown the completion queue after the server.
JLOG(journal_.debug()) << "Server has been shutdown";
// Always shutdown the completion queue after the server. This call allows
// cq_.Next() to return false, once all events posted to the completion
// queue have been processed. See handleRpcs() for more details.
cq_->Shutdown();
JLOG(journal_.debug()) << "Completion Queue has been shutdown";
}
void
@@ -265,21 +279,33 @@ GRPCServerImpl::handleRpcs()
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
// When cq_.Next(...) returns false, all work has been completed and the
// loop can exit. When the server is shutdown, each CallData object that is
// listening for a request is forceably cancelled, and is returned by
// cq_->Next() with ok set to false. Then, each CallData object processing
// a request must complete (by sending data to the client), each of which
// will be returned from cq_->Next() with ok set to true. After all
// cancelled listeners and all CallData objects processing requests are
// returned via cq_->Next(), cq_->Next() will return false, causing the
// loop to exit.
while (cq_->Next(&tag, &ok))
{
auto ptr = static_cast<Processor*>(tag);
// if ok is false, event was terminated as part of a shutdown sequence
// need to abort any further processing
JLOG(journal_.trace()) << "Processing CallData object."
<< " ptr = " << ptr
<< " ok = " << ok;
if (!ok)
{
// abort first, then erase. Otherwise, erase can delete object
ptr->abort();
JLOG(journal_.debug()) << "Request listener cancelled. "
<< "Destroying object";
erase(ptr);
}
else
{
if (!ptr->isFinished())
{
JLOG(journal_.debug()) << "Received new request. Processing";
// ptr is now processing a request, so create a new CallData
// object to handle additional requests
auto cloned = ptr->clone();
@@ -289,10 +315,13 @@ GRPCServerImpl::handleRpcs()
}
else
{
JLOG(journal_.debug()) << "Sent response. Destroying object";
erase(ptr);
}
}
}
JLOG(journal_.debug()) << "Completion Queue drained";
}
// create a CallData instance for each RPC
@@ -306,58 +335,76 @@ GRPCServerImpl::setupListeners()
};
{
using cd = CallData<rpc::v1::GetFeeRequest, rpc::v1::GetFeeResponse>;
using cd = CallData<org::xrpl::rpc::v1::GetFeeRequest, org::xrpl::rpc::v1::GetFeeResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetFee,
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetFee,
doFeeGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeReferenceRPC));
}
{
using cd = CallData<
rpc::v1::GetAccountInfoRequest,
rpc::v1::GetAccountInfoResponse>;
org::xrpl::rpc::v1::GetAccountInfoRequest,
org::xrpl::rpc::v1::GetAccountInfoResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetAccountInfo,
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetAccountInfo,
doAccountInfoGrpc,
RPC::NEEDS_CURRENT_LEDGER,
RPC::NO_CONDITION,
Resource::feeReferenceRPC));
}
{
using cd = CallData<rpc::v1::GetTxRequest, rpc::v1::GetTxResponse>;
using cd = CallData<
org::xrpl::rpc::v1::GetTransactionRequest,
org::xrpl::rpc::v1::GetTransactionResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetTx,
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::RequestGetTransaction,
doTxGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeReferenceRPC));
}
{
using cd = CallData<
rpc::v1::SubmitTransactionRequest,
rpc::v1::SubmitTransactionResponse>;
org::xrpl::rpc::v1::SubmitTransactionRequest,
org::xrpl::rpc::v1::SubmitTransactionResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&rpc::v1::XRPLedgerAPIService::AsyncService::
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
RequestSubmitTransaction,
doSubmitGrpc,
RPC::NEEDS_CURRENT_LEDGER,
Resource::feeMediumBurdenRPC));
}
{
using cd = CallData<
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest,
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse>;
addToRequests(std::make_shared<cd>(
service_,
*cq_,
app_,
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
RequestGetAccountTransactionHistory,
doAccountTxGrpc,
RPC::NO_CONDITION,
Resource::feeMediumBurdenRPC));
}
return requests;
};
@@ -368,6 +415,8 @@ GRPCServerImpl::start()
if (serverAddress_.empty())
return false;
JLOG(journal_.info()) << "Starting gRPC server at " << serverAddress_;
grpc::ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(serverAddress_, grpc::InsecureServerCredentials());

View File

@@ -32,7 +32,7 @@
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/Tuning.h>
#include "rpc/v1/xrp_ledger.grpc.pb.h"
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
#include <grpcpp/grpcpp.h>
namespace ripple {
@@ -54,10 +54,6 @@ public:
virtual void
process() = 0;
// abort processing this request. called when server shutsdown
virtual void
abort() = 0;
// create a new instance of this CallData object, with the same type
//(same template parameters) as original. This is called when a CallData
// object starts processing a request. Creating a new instance allows the
@@ -81,7 +77,7 @@ private:
std::vector<std::shared_ptr<Processor>> requests_;
// The gRPC service defined by the .proto files
rpc::v1::XRPLedgerAPIService::AsyncService service_;
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService service_;
std::unique_ptr<grpc::Server> server_;
@@ -89,12 +85,14 @@ private:
std::string serverAddress_;
beast::Journal journal_;
// typedef for function to bind a listener
// This is always of the form:
// rpc::v1::XRPLedgerAPIService::AsyncService::Request[RPC NAME]
// org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::Request[RPC NAME]
template <class Request, class Response>
using BindListener = std::function<void(
rpc::v1::XRPLedgerAPIService::AsyncService&,
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService&,
grpc::ServerContext*,
Request*,
grpc::ServerAsyncResponseWriter<Response>*,
@@ -142,7 +140,7 @@ private:
private:
// The means of communication with the gRPC runtime for an asynchronous
// server.
rpc::v1::XRPLedgerAPIService::AsyncService& service_;
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService& service_;
// The producer-consumer queue for asynchronous server notifications.
grpc::ServerCompletionQueue& cq_;
@@ -153,16 +151,14 @@ private:
grpc::ServerContext ctx_;
// true if finished processing request
bool finished_;
// Note, this variable does not need to be atomic, since it is
// currently only accessed from one thread. However, isFinished(),
// which returns the value of this variable, is public facing. In the
// interest of avoiding future concurrency bugs, we make it atomic.
std::atomic_bool finished_;
Application& app_;
// mutex for signaling abort
std::mutex mut_;
// whether the call should be aborted, due to server shutdown
bool aborted_;
// What we get from the client.
Request request_;
@@ -191,7 +187,7 @@ private:
// asynchronous server) and the completion queue "cq" used for
// asynchronous communication with the gRPC runtime.
explicit CallData(
rpc::v1::XRPLedgerAPIService::AsyncService& service,
org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService& service,
grpc::ServerCompletionQueue& cq,
Application& app,
BindListener<Request, Response> bindListener,
@@ -210,9 +206,6 @@ private:
virtual bool
isFinished() override;
virtual void
abort() override;
std::shared_ptr<Processor>
clone() override;

View File

@@ -434,15 +434,21 @@ public:
bool binary, bool count, bool bUnlimited);
// Client information retrieval functions.
using NetworkOPs::AccountTxMarker;
using NetworkOPs::AccountTxs;
AccountTxs getAccountTxs (
AccountID const& account,
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
std::uint32_t offset, int limit, bool bUnlimited) override;
AccountTxs getTxsAccount (
AccountID const& account, std::int32_t minLedger,
std::int32_t maxLedger, bool forward, Json::Value& token, int limit,
AccountTxs
getTxsAccount(
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited) override;
using NetworkOPs::txnMetaLedgerType;
@@ -454,11 +460,16 @@ public:
std::int32_t maxLedger, bool descending, std::uint32_t offset,
int limit, bool bUnlimited) override;
MetaTxsList
getTxsAccountB (
AccountID const& account, std::int32_t minLedger,
std::int32_t maxLedger, bool forward, Json::Value& token,
int limit, bool bUnlimited) override;
getTxsAccountB(
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited) override;
//
// Monitoring: publisher side.
@@ -2193,7 +2204,7 @@ std::vector<NetworkOPsImp::txnMetaLedgerType> NetworkOPsImp::getAccountTxsB (
rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or (0));
ret.emplace_back (
strHex (rawTxn), strHex (txnMeta), seq);
std::move(rawTxn), std::move(txnMeta), seq);
}
}
@@ -2201,59 +2212,80 @@ std::vector<NetworkOPsImp::txnMetaLedgerType> NetworkOPsImp::getAccountTxsB (
}
NetworkOPsImp::AccountTxs
NetworkOPsImp::getTxsAccount (
AccountID const& account, std::int32_t minLedger,
std::int32_t maxLedger, bool forward, Json::Value& token,
int limit, bool bUnlimited)
NetworkOPsImp::getTxsAccount(
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited)
{
static std::uint32_t const page_length (200);
static std::uint32_t const page_length(200);
Application& app = app_;
NetworkOPsImp::AccountTxs ret;
auto bound = [&ret, &app](
std::uint32_t ledger_index,
std::string const& status,
Blob const& rawTxn,
Blob const& rawMeta)
{
convertBlobsToTxResult (
ret, ledger_index, status, rawTxn, rawMeta, app);
std::uint32_t ledger_index,
std::string const& status,
Blob const& rawTxn,
Blob const& rawMeta) {
convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app);
};
accountTxPage(app_.getTxnDB (), app_.accountIDCache(),
std::bind(saveLedgerAsync, std::ref(app_),
std::placeholders::_1), bound, account, minLedger,
maxLedger, forward, token, limit, bUnlimited,
page_length);
accountTxPage(
app_.getTxnDB(),
app_.accountIDCache(),
std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1),
bound,
account,
minLedger,
maxLedger,
forward,
marker,
limit,
bUnlimited,
page_length);
return ret;
}
NetworkOPsImp::MetaTxsList
NetworkOPsImp::getTxsAccountB (
AccountID const& account, std::int32_t minLedger,
std::int32_t maxLedger, bool forward, Json::Value& token,
int limit, bool bUnlimited)
NetworkOPsImp::getTxsAccountB(
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited)
{
static const std::uint32_t page_length (500);
static const std::uint32_t page_length(500);
MetaTxsList ret;
auto bound = [&ret](
std::uint32_t ledgerIndex,
std::string const& status,
Blob const& rawTxn,
Blob const& rawMeta)
{
ret.emplace_back (strHex(rawTxn), strHex (rawMeta), ledgerIndex);
std::uint32_t ledgerIndex,
std::string const& status,
Blob const& rawTxn,
Blob const& rawMeta) {
ret.emplace_back(std::move(rawTxn), std::move(rawMeta), ledgerIndex);
};
accountTxPage(app_.getTxnDB (), app_.accountIDCache(),
std::bind(saveLedgerAsync, std::ref(app_),
std::placeholders::_1), bound, account, minLedger,
maxLedger, forward, token, limit, bUnlimited,
page_length);
accountTxPage(
app_.getTxnDB(),
app_.accountIDCache(),
std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1),
bound,
account,
minLedger,
maxLedger,
forward,
marker,
limit,
bUnlimited,
page_length);
return ret;
}

View File

@@ -212,6 +212,12 @@ public:
virtual void updateLocalTx (ReadView const& newValidLedger) = 0;
virtual std::size_t getLocalTxCount () = 0;
struct AccountTxMarker
{
uint32_t ledgerSeq = 0;
uint32_t txnSeq = 0;
};
// client information retrieval functions
using AccountTx = std::pair<std::shared_ptr<Transaction>, TxMeta::pointer>;
using AccountTxs = std::vector<AccountTx>;
@@ -221,21 +227,32 @@ public:
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
std::uint32_t offset, int limit, bool bUnlimited) = 0;
virtual AccountTxs getTxsAccount (
virtual AccountTxs
getTxsAccount(
AccountID const& account,
std::int32_t minLedger, std::int32_t maxLedger, bool forward,
Json::Value& token, int limit, bool bUnlimited) = 0;
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited) = 0;
using txnMetaLedgerType = std::tuple<std::string, std::string, std::uint32_t>;
using txnMetaLedgerType = std::tuple<Blob, Blob, std::uint32_t>;
using MetaTxsList = std::vector<txnMetaLedgerType>;
virtual MetaTxsList getAccountTxsB (AccountID const& account,
std::int32_t minLedger, std::int32_t maxLedger, bool descending,
std::uint32_t offset, int limit, bool bUnlimited) = 0;
virtual MetaTxsList getTxsAccountB (AccountID const& account,
std::int32_t minLedger, std::int32_t maxLedger, bool forward,
Json::Value& token, int limit, bool bUnlimited) = 0;
virtual MetaTxsList
getTxsAccountB(
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
std::optional<AccountTxMarker>& marker,
int limit,
bool bUnlimited) = 0;
//--------------------------------------------------------------------------
//

View File

@@ -61,24 +61,25 @@ saveLedgerAsync (Application& app, std::uint32_t seq)
}
void
accountTxPage (
accountTxPage(
DatabaseCon& connection,
AccountIDCache const& idCache,
std::function<void (std::uint32_t)> const& onUnsavedLedger,
std::function<void (std::uint32_t,
std::string const&,
Blob const&,
Blob const&)> const& onTransaction,
std::function<void(std::uint32_t)> const& onUnsavedLedger,
std::function<void(
std::uint32_t,
std::string const&,
Blob const&,
Blob const&)> const& onTransaction,
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
Json::Value& token,
std::optional<NetworkOPs::AccountTxMarker>& marker,
int limit,
bool bAdmin,
std::uint32_t page_length)
{
bool lookingForMarker = token.isObject();
bool lookingForMarker = marker.has_value();
std::uint32_t numberOfResults;
@@ -97,24 +98,14 @@ accountTxPage (
if (lookingForMarker)
{
try
{
if (!token.isMember(jss::ledger) || !token.isMember(jss::seq))
return;
findLedger = token[jss::ledger].asInt();
findSeq = token[jss::seq].asInt();
}
catch (std::exception const&)
{
return;
}
findLedger = marker->ledgerSeq;
findSeq = marker->txnSeq;
}
// We're using the token reference both for passing inputs and outputs, so
// we need to clear it in between.
token = Json::nullValue;
// marker is also an output parameter, so need to reset
marker.reset();
static std::string const prefix (
static std::string const prefix(
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
Status,RawTxn,TxnMeta
FROM AccountTransactions INNER JOIN Transactions
@@ -128,22 +119,20 @@ accountTxPage (
if (forward && (findLedger == 0))
{
sql = boost::str (boost::format(
prefix +
(R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
sql = boost::str(
boost::format(
prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
ORDER BY AccountTransactions.LedgerSeq ASC,
AccountTransactions.TxnSeq ASC
LIMIT %u;)"))
% idCache.toBase58(account)
% minLedger
% maxLedger
% queryLimit);
LIMIT %u;)")) %
idCache.toBase58(account) % minLedger % maxLedger % queryLimit);
}
else if (forward && (findLedger != 0))
{
auto b58acct = idCache.toBase58(account);
sql = boost::str (boost::format(
(R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
sql = boost::str(
boost::format((
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
Status,RawTxn,TxnMeta
FROM AccountTransactions, Transactions WHERE
(AccountTransactions.TransID = Transactions.TransID AND
@@ -157,33 +146,26 @@ accountTxPage (
ORDER BY AccountTransactions.LedgerSeq ASC,
AccountTransactions.TxnSeq ASC
LIMIT %u;
)"))
% b58acct
% (findLedger + 1)
% maxLedger
% b58acct
% findLedger
% findSeq
% queryLimit);
)")) %
b58acct % (findLedger + 1) % maxLedger % b58acct % findLedger %
findSeq % queryLimit);
}
else if (!forward && (findLedger == 0))
{
sql = boost::str (boost::format(
prefix +
(R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
sql = boost::str(
boost::format(
prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
ORDER BY AccountTransactions.LedgerSeq DESC,
AccountTransactions.TxnSeq DESC
LIMIT %u;)"))
% idCache.toBase58(account)
% minLedger
% maxLedger
% queryLimit);
LIMIT %u;)")) %
idCache.toBase58(account) % minLedger % maxLedger % queryLimit);
}
else if (!forward && (findLedger != 0))
{
auto b58acct = idCache.toBase58(account);
sql = boost::str (boost::format(
(R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
sql = boost::str(
boost::format((
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
Status,RawTxn,TxnMeta
FROM AccountTransactions, Transactions WHERE
(AccountTransactions.TransID = Transactions.TransID AND
@@ -197,24 +179,19 @@ accountTxPage (
ORDER BY AccountTransactions.LedgerSeq DESC,
AccountTransactions.TxnSeq DESC
LIMIT %u;
)"))
% b58acct
% minLedger
% (findLedger - 1)
% b58acct
% findLedger
% findSeq
% queryLimit);
)")) %
b58acct % minLedger % (findLedger - 1) % b58acct % findLedger %
findSeq % queryLimit);
}
else
{
assert (false);
assert(false);
// sql is empty
return;
}
{
auto db (connection.checkoutDb());
auto db(connection.checkoutDb());
Blob rawData;
Blob rawMeta;
@@ -222,55 +199,59 @@ accountTxPage (
boost::optional<std::uint64_t> ledgerSeq;
boost::optional<std::uint32_t> txnSeq;
boost::optional<std::string> status;
soci::blob txnData (*db);
soci::blob txnMeta (*db);
soci::blob txnData(*db);
soci::blob txnMeta(*db);
soci::indicator dataPresent, metaPresent;
soci::statement st = (db->prepare << sql,
soci::into (ledgerSeq),
soci::into (txnSeq),
soci::into (status),
soci::into (txnData, dataPresent),
soci::into (txnMeta, metaPresent));
soci::statement st =
(db->prepare << sql,
soci::into(ledgerSeq),
soci::into(txnSeq),
soci::into(status),
soci::into(txnData, dataPresent),
soci::into(txnMeta, metaPresent));
st.execute ();
st.execute();
while (st.fetch ())
while (st.fetch())
{
if (lookingForMarker)
{
if (findLedger == ledgerSeq.value_or (0) &&
findSeq == txnSeq.value_or (0))
if (findLedger == ledgerSeq.value_or(0) &&
findSeq == txnSeq.value_or(0))
{
lookingForMarker = false;
}
}
else if (numberOfResults == 0)
{
token = Json::objectValue;
token[jss::ledger] = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or (0));
token[jss::seq] = txnSeq.value_or (0);
marker = {
rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
txnSeq.value_or(0)};
break;
}
if (!lookingForMarker)
{
if (dataPresent == soci::i_ok)
convert (txnData, rawData);
convert(txnData, rawData);
else
rawData.clear ();
rawData.clear();
if (metaPresent == soci::i_ok)
convert (txnMeta, rawMeta);
convert(txnMeta, rawMeta);
else
rawMeta.clear ();
rawMeta.clear();
// Work around a bug that could leave the metadata missing
if (rawMeta.size() == 0)
onUnsavedLedger(ledgerSeq.value_or (0));
onUnsavedLedger(ledgerSeq.value_or(0));
onTransaction(rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or (0)),
*status, rawData, rawMeta);
onTransaction(
rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
*status,
rawData,
rawMeta);
--numberOfResults;
}
}
@@ -278,5 +259,4 @@ accountTxPage (
return;
}
}

View File

@@ -44,23 +44,23 @@ void
saveLedgerAsync (Application& app, std::uint32_t seq);
void
accountTxPage (
DatabaseCon& database,
accountTxPage(
DatabaseCon& connection,
AccountIDCache const& idCache,
std::function<void (std::uint32_t)> const& onUnsavedLedger,
std::function<void (std::uint32_t,
std::string const&,
Blob const&,
Blob const&)> const&,
std::function<void(std::uint32_t)> const& onUnsavedLedger,
std::function<void(
std::uint32_t,
std::string const&,
Blob const&,
Blob const&)> const& onTransaction,
AccountID const& account,
std::int32_t minLedger,
std::int32_t maxLedger,
bool forward,
Json::Value& token,
std::optional<NetworkOPs::AccountTxMarker>& marker,
int limit,
bool bAdmin,
std::uint32_t pageLength);
std::uint32_t page_length);
}
#endif

View File

@@ -0,0 +1,14 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
// A representation of an account address
// Next field: 2
message AccountAddress
{
// base58 encoding of an account
string address = 1;
}

View File

@@ -1,29 +1,32 @@
syntax = "proto3";
package rpc.v1;
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
message CurrencyAmount {
oneof amount {
import "org/xrpl/rpc/v1/account.proto";
// Next field: 3
message CurrencyAmount
{
oneof amount
{
XRPDropsAmount xrp_amount = 1;
IssuedCurrencyAmount issued_currency_amount = 2;
}
}
// A representation of an amount of XRP.
message XRPDropsAmount {
uint64 drops = 1;
}
// A representation of an account address
message AccountAddress {
//base58 encoding of an account
string address = 1;
// Next field: 2
message XRPDropsAmount
{
uint64 drops = 1 [jstype=JS_STRING];
}
// A representation of an amount of issued currency.
message IssuedCurrencyAmount {
// Next field: 4
message IssuedCurrencyAmount
{
// The currency used to value the amount.
Currency currency = 1;
@@ -34,8 +37,9 @@ message IssuedCurrencyAmount {
AccountAddress issuer = 3;
}
message Currency {
// Next field: 3
message Currency
{
// 3 character ASCII code
string name = 1;

View File

@@ -0,0 +1,476 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/amount.proto";
import "org/xrpl/rpc/v1/account.proto";
// These fields are used in many different messsage types. They can be present
// in one or more transactions, as well as metadata of one or more transactions.
// Each is defined as its own message type with a single field "value", to
// ensure the field is the correct type everywhere it's used
// *** Messages wrapping uint32 ***
message CancelAfter
{
// time in seconds since Ripple epoch
uint32 value = 1;
}
message ClearFlag
{
uint32 value = 1;
}
message CloseTime
{
// time in seconds since Ripple epoch
uint32 value = 1;
}
message Date
{
// time in seconds since Ripple epoch
uint32 value = 1;
}
message DestinationTag
{
uint32 value = 1;
}
message Expiration
{
// time in seconds since Ripple epoch
uint32 value = 1;
}
message FinishAfter
{
// time in seconds since Ripple epoch
uint32 value = 1;
}
message Flags
{
uint32 value = 1;
}
message HighQualityIn
{
uint32 value = 1;
}
message HighQualityOut
{
uint32 value = 1;
}
message LastLedgerSequence
{
uint32 value = 1;
}
message LowQualityIn
{
uint32 value = 1;
}
message LowQualityOut
{
uint32 value = 1;
}
message OfferSequence
{
uint32 value = 1;
}
message OwnerCount
{
uint32 value = 1;
}
message PreviousTransactionLedgerSequence
{
uint32 value = 1;
}
message QualityIn
{
uint32 value = 1;
}
message QualityOut
{
uint32 value = 1;
}
message ReferenceFeeUnits
{
uint32 value = 1;
}
message ReserveBase
{
// in drops
uint32 value = 1;
}
message ReserveIncrement
{
// in drops
uint32 value = 1;
}
message Sequence
{
uint32 value = 1;
}
message SetFlag
{
uint32 value = 1;
}
message SettleDelay
{
uint32 value = 1;
}
message SignerListID
{
uint32 value = 1;
}
message SignerQuorum
{
uint32 value = 1;
}
message SignerWeight
{
// is actually uint16
uint32 value = 1;
}
message SourceTag
{
uint32 value = 1;
}
message TickSize
{
// is actually uint8
uint32 value = 1;
}
message TransferRate
{
uint32 value = 1;
}
// *** Messages wrapping uint64 ***
message BaseFee
{
// in drops
uint64 value = 1 [jstype=JS_STRING];
}
message BookNode
{
uint64 value = 1 [jstype=JS_STRING];
}
message DestinationNode
{
uint64 value = 1 [jstype=JS_STRING];
}
message HighNode
{
uint64 value = 1 [jstype=JS_STRING];
}
message IndexNext
{
uint64 value = 1 [jstype=JS_STRING];
}
message IndexPrevious
{
uint64 value = 1 [jstype=JS_STRING];
}
message LowNode
{
uint64 value = 1 [jstype=JS_STRING];
}
message OwnerNode
{
uint64 value = 1 [jstype=JS_STRING];
}
// *** Messages wrapping 16 bytes ***
message EmailHash
{
bytes value = 1;
}
// *** Messages wrapping 20 bytes ***
message TakerGetsIssuer
{
// 20 bytes
bytes value = 1;
}
message TakerPaysIssuer
{
// 20 bytes
bytes value = 1;
}
// *** Messages wrapping 32 bytes ***
message AccountTransactionID
{
// 32 bytes
bytes value = 1;
}
message BookDirectory
{
// 32 btes
bytes value = 1;
}
message Channel
{
// 32 bytes
bytes value = 1;
}
message CheckID
{
// 32 bytes
bytes value = 1;
}
message Hash
{
// 32 bytes
bytes value = 1;
}
message Index
{
// 32 bytes
bytes value = 1;
}
message InvoiceID
{
// 32 bytes
bytes value = 1;
}
message PreviousTransactionID
{
// 32 bytes
bytes value = 1;
}
message RootIndex
{
// 32 bytes
bytes value = 1;
}
// *** Messages wrapping variable length byte arrays ***
message Condition
{
bytes value = 1;
}
message Fulfillment
{
bytes value = 1;
}
message MemoData
{
bytes value = 1;
}
message MemoFormat
{
bytes value = 1;
}
message MemoType
{
bytes value = 1;
}
message MessageKey
{
bytes value = 1;
}
message PublicKey
{
bytes value = 1;
}
message PaymentChannelSignature
{
bytes value = 1;
}
message SigningPublicKey
{
bytes value = 1;
}
message TransactionSignature
{
bytes value = 1;
}
// *** Messages wrapping a Currency value ***
message TakerGetsCurreny
{
Currency value = 1;
}
message TakerPaysCurrency
{
Currency value = 1;
}
// *** Messages wrapping a CurrencyAmount ***
message Amount
{
// Note, CurrencyAmount is a oneof, that can represent an XRP drops amount
// or an Issued Currency amount. However, in some transaction types/ledger
// objects, this value can only be in drops. For instance, the Amount field
// of a Payment transaction can be specified in XRP drops or an Issued
// Currency amount, but the Amount field of a PaymentChannelClaim
// transaction can only be an XRP drops amount.
CurrencyAmount value = 1;
}
message Balance
{
CurrencyAmount value = 1;
}
message DeliverMin
{
CurrencyAmount value = 1;
}
message DeliveredAmount
{
CurrencyAmount value = 1;
}
message HighLimit
{
CurrencyAmount value = 1;
}
message LimitAmount
{
CurrencyAmount value = 1;
}
message LowLimit
{
CurrencyAmount value = 1;
}
message SendMax
{
CurrencyAmount value = 1;
}
message TakerGets
{
CurrencyAmount value = 1;
}
message TakerPays
{
CurrencyAmount value = 1;
}
// *** Messages wrapping an AccountAddress ***
message Account
{
AccountAddress value = 1;
}
message Authorize
{
AccountAddress value = 1;
}
message Destination
{
AccountAddress value = 1;
}
message Owner
{
AccountAddress value = 1;
}
message RegularKey
{
AccountAddress value = 1;
}
message Unauthorize
{
AccountAddress value = 1;
}
// *** Messages wrapping a string ***
message Domain
{
string value = 1;
}
// *** Aggregate type messages
// Next field: 3
message SignerEntry
{
Account account = 1;
SignerWeight signer_weight = 2;
}

View File

@@ -1,12 +1,19 @@
syntax = "proto3";
package rpc.v1;
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "rpc/v1/ledger_objects.proto";
import "rpc/v1/amount.proto";
import "org/xrpl/rpc/v1/ledger_objects.proto";
import "org/xrpl/rpc/v1/amount.proto";
import "org/xrpl/rpc/v1/account.proto";
import "org/xrpl/rpc/v1/ledger.proto";
import "org/xrpl/rpc/v1/common.proto";
// A request to get info about an account.
message GetAccountInfoRequest {
// Next field: 6
message GetAccountInfoRequest
{
// The address to get info about.
AccountAddress account = 1;
@@ -19,25 +26,10 @@ message GetAccountInfoRequest {
bool signer_lists = 5;
}
message LedgerSpecifier {
enum Shortcut {
SHORTCUT_UNSPECIFIED = 0;
SHORTCUT_VALIDATED = 1;
SHORTCUT_CLOSED = 2;
SHORTCUT_CURRENT = 3;
}
oneof ledger {
Shortcut shortcut = 1;
uint32 sequence = 2;
// 32 bytes
bytes hash = 3;
}
}
// Response to GetAccountInfo RPC
message GetAccountInfoResponse {
// Next field: 6
message GetAccountInfoResponse
{
AccountRoot account_data = 1;
SignerList signer_list = 2;
@@ -50,8 +42,9 @@ message GetAccountInfoResponse {
}
// Aggregate data about queued transactions
message QueueData {
// Next field: 7
message QueueData
{
uint32 txn_count = 1;
bool auth_change_queued = 2;
@@ -66,16 +59,18 @@ message QueueData {
}
// Data about a single queued transaction
message QueuedTransaction {
// Next field: 7
message QueuedTransaction
{
bool auth_change = 1;
XRPDropsAmount fee = 2;
uint64 fee_level = 3;
uint64 fee_level = 3 [jstype=JS_STRING];
XRPDropsAmount max_spend_drops = 4;
uint32 sequence = 5;
Sequence sequence = 5;
uint32 last_ledger_sequence = 6;
LastLedgerSequence last_ledger_sequence = 6;
}

View File

@@ -0,0 +1,68 @@
syntax = "proto3";
import "org/xrpl/rpc/v1/get_transaction.proto";
import "org/xrpl/rpc/v1/account.proto";
import "org/xrpl/rpc/v1/ledger.proto";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
// Next field: 8
message GetAccountTransactionHistoryRequest
{
AccountAddress account = 1;
// What ledger to include results from. Specifying a not yet validated
// ledger results in an error. Not specifying a ledger uses the entire
// range of validated ledgers available to the server.
oneof ledger
{
LedgerSpecifier ledger_specifier = 2;
LedgerRange ledger_range = 3;
};
// Return results as binary blobs. Defaults to false.
bool binary = 4;
// If set to true, returns values indexed by older ledger first.
// Default to false.
bool forward = 5;
// Limit the number of results. Server may choose a lower limit.
// If this value is 0, the limit is ignored and the number of results
// returned is determined by the server
uint32 limit = 6;
// Marker to resume where previous request left off
// Used for pagination
Marker marker = 7;
}
// Next field: 8
message GetAccountTransactionHistoryResponse
{
AccountAddress account = 1;
uint32 ledger_index_min = 2;
uint32 ledger_index_max = 3;
uint32 limit = 4;
Marker marker = 5;
repeated GetTransactionResponse transactions = 6;
bool validated = 7;
}
// Next field: 3
message Marker
{
uint32 ledger_index = 1;
uint32 account_sequence = 2;
}

View File

@@ -0,0 +1,57 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/amount.proto";
// A request for the current transaction fee on the ledger.
// Next field: 1
message GetFeeRequest
{
}
// Response to a GetFee RPC
// Next field: 8
message GetFeeResponse
{
uint64 current_ledger_size = 1 [jstype=JS_STRING];
uint64 current_queue_size = 2 [jstype=JS_STRING];
Fee fee = 3;
uint64 expected_ledger_size = 4 [jstype=JS_STRING];
uint32 ledger_current_index = 5;
FeeLevels levels = 6;
uint64 max_queue_size = 7 [jstype=JS_STRING];
}
// Next field: 5
message Fee
{
XRPDropsAmount base_fee = 1;
XRPDropsAmount median_fee = 2;
XRPDropsAmount minimum_fee = 3;
XRPDropsAmount open_ledger_fee = 4;
}
// Next field: 5
message FeeLevels
{
uint64 median_level = 1 [jstype=JS_STRING];
uint64 minimum_level = 2 [jstype=JS_STRING];
uint64 open_ledger_level = 3 [jstype=JS_STRING];
uint64 reference_level = 4 [jstype=JS_STRING];
}

View File

@@ -0,0 +1,56 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/meta.proto";
import "org/xrpl/rpc/v1/ledger.proto";
import "org/xrpl/rpc/v1/transaction.proto";
import "org/xrpl/rpc/v1/common.proto";
// Next field: 4
message GetTransactionRequest {
// hash of the transaction. 32 bytes
// ATTN: this is in binary, not hex. The JSON API accepts a hex string for
// a transaction hash, but here we need that hex string converted into its
// binary form. Each pair of hex characters should be converted into its
// corresponding byte. For example, the 4 character hex string "00FF"
// should be converted to a 2 byte array: [0, 255]
bytes hash = 1;
// if true, return data in binary format. defaults to false
bool binary = 2;
// search only specified range. optional
LedgerRange ledger_range = 3;
}
// Next field: 9
message GetTransactionResponse {
oneof serialized_transaction {
Transaction transaction = 1;
// Variable length
bytes transaction_binary = 2;
};
// Sequence number of ledger that contains this transaction
uint32 ledger_index = 3;
// 32 bytes
bytes hash = 4;
// whether the ledger has been validated
bool validated = 5;
// metadata about the transaction
oneof serialized_meta {
Meta meta = 6;
// Variable length
bytes meta_binary = 7;
}
Date date = 8;
}

View File

@@ -0,0 +1,37 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
// Next field: 4
message LedgerSpecifier
{
// Next field: 4
enum Shortcut
{
SHORTCUT_UNSPECIFIED = 0;
SHORTCUT_VALIDATED = 1;
SHORTCUT_CLOSED = 2;
SHORTCUT_CURRENT = 3;
}
oneof ledger
{
Shortcut shortcut = 1;
uint32 sequence = 2;
// 32 bytes
bytes hash = 3;
}
}
// Next field: 3
message LedgerRange
{
uint32 ledger_index_min = 1;
// Note, if ledger_index_min is non-zero and ledger_index_max is 0, the
// software will use the max validated ledger in place of ledger_index_max
uint32 ledger_index_max = 2;
};

View File

@@ -0,0 +1,331 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/common.proto";
// Next field: 13
message LedgerObject
{
oneof object
{
AccountRoot account_root = 1;
Amendments amendments = 2;
Check check = 3;
DepositPreauthObject deposit_preauth = 4;
DirectoryNode directory_node = 5;
Escrow escrow = 6;
FeeSettings fee_settings = 7;
LedgerHashes ledger_hashes = 8;
Offer offer = 9;
PayChannel pay_channel = 10;
RippleState ripple_state = 11;
SignerList signer_list = 12;
}
}
// Next field: 13
enum LedgerEntryType
{
LEDGER_ENTRY_TYPE_UNSPECIFIED = 0;
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT = 1;
LEDGER_ENTRY_TYPE_AMENDMENTS = 2;
LEDGER_ENTRY_TYPE_CHECK = 3;
LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH = 4;
LEDGER_ENTRY_TYPE_DIRECTORY_NODE = 5;
LEDGER_ENTRY_TYPE_ESCROW = 6;
LEDGER_ENTRY_TYPE_FEE_SETTINGS = 7;
LEDGER_ENTRY_TYPE_LEDGER_HASHES = 8;
LEDGER_ENTRY_TYPE_OFFER = 9;
LEDGER_ENTRY_TYPE_PAY_CHANNEL = 10;
LEDGER_ENTRY_TYPE_RIPPLE_STATE = 11;
LEDGER_ENTRY_TYPE_SIGNER_LIST = 12;
}
// Next field: 15
message AccountRoot
{
Account account = 1;
Balance balance = 2;
Sequence sequence = 3;
Flags flags = 4;
OwnerCount owner_count = 5;
PreviousTransactionID previous_transaction_id = 6;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 7;
AccountTransactionID account_transaction_id = 8;
Domain domain = 9;
EmailHash email_hash = 10;
MessageKey message_key = 11;
RegularKey regular_key = 12;
TickSize tick_size = 13;
TransferRate transfer_rate = 14;
}
// Next field: 4
message Amendments
{
// Next field: 2
message Amendment
{
// 32 bytes
bytes value = 1;
}
// Next field: 3
message Majority
{
Amendment amendment = 1;
CloseTime close_time = 2;
}
repeated Amendment amendments = 1;
repeated Majority majorities = 2;
Flags flags = 3;
}
// Next field: 14
message Check
{
Account account = 1;
Destination destination = 2;
Flags flags = 3;
OwnerNode owner_node = 4;
PreviousTransactionID previous_transaction_id = 5;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 6;
SendMax send_max = 7;
Sequence sequence = 8;
DestinationNode destination_node = 9;
DestinationTag destination_tag = 10;
Expiration expiration = 11;
InvoiceID invoice_id = 12;
SourceTag source_tag = 13;
}
// Next field: 7
message DepositPreauthObject
{
Account account = 1;
Authorize authorize = 2;
Flags flags = 3;
OwnerNode owner_node = 4;
PreviousTransactionID previous_transaction_id = 5;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 6;
}
// Next field: 11
message DirectoryNode
{
Flags flags = 1;
RootIndex root_index = 2;
repeated Index indexes = 3;
IndexNext index_next = 4;
IndexPrevious index_previous = 5;
Owner owner = 6;
TakerPaysCurrency taker_pays_currency = 7;
TakerPaysIssuer taker_pays_issuer = 8;
TakerGetsCurreny taker_gets_currency = 9;
TakerGetsIssuer taker_gets_issuer = 10;
}
// Next field: 14
message Escrow
{
Account account = 1;
Destination destination = 2;
Amount amount = 3;
Condition condition = 4;
CancelAfter cancel_after = 5;
FinishAfter finish_after = 6;
Flags flags = 7;
SourceTag source_tag = 8;
DestinationTag destination_tag = 9;
OwnerNode owner_node = 10;
DestinationNode destination_node = 11;
PreviousTransactionID previous_transaction_id = 12;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 13;
}
// Next field: 6
message FeeSettings
{
BaseFee base_fee = 1;
ReferenceFeeUnits reference_fee_units = 2;
ReserveBase reserve_base = 3;
ReserveIncrement reserve_increment = 4;
Flags flags = 5;
}
// Next field: 4
message LedgerHashes
{
LastLedgerSequence last_ledger_sequence = 1;
repeated Hash hashes = 2;
Flags flags = 3;
}
// Next field: 12
message Offer
{
Account account = 1;
Sequence sequence = 2;
Flags flags = 3;
TakerPays taker_pays = 4;
TakerGets taker_gets = 5;
BookDirectory book_directory = 6;
BookNode book_node = 7;
OwnerNode owner_node = 8;
Expiration expiration = 9;
PreviousTransactionID previous_transaction_id = 10;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 11;
}
// Next field: 13
message PayChannel
{
Account account = 1;
Destination destination = 2;
Amount amount = 3;
Balance balance = 4;
PublicKey public_key = 5;
SettleDelay settle_delay = 6;
OwnerNode owner_node = 7;
PreviousTransactionID previous_transaction_id = 8;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 9;
Flags flags = 10;
Expiration expiration = 11;
CancelAfter cancel_after = 12;
SourceTag source_tag = 13;
DestinationTag destination_tag = 14;
}
// Next field: 13
message RippleState
{
Balance balance = 1;
Flags flags = 2;
LowLimit low_limit = 3;
HighLimit high_limit = 4;
LowNode low_node = 5;
HighNode high_node = 6;
LowQualityIn low_quality_in = 7;
LowQualityOut low_quality_out = 8;
HighQualityIn high_quality_in = 9;
HighQualityOut high_quality_out = 10;
PreviousTransactionID previous_transaction_id = 11;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 12;
}
// Next field: 8
message SignerList
{
Flags flags = 1;
PreviousTransactionID previous_transaction_id = 2;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 3;
OwnerNode owner_node = 4;
repeated SignerEntry signer_entries = 5;
SignerListID signer_list_id = 6;
SignerQuorum signer_quorum = 7;
}

View File

@@ -1,25 +1,32 @@
syntax = "proto3";
package rpc.v1;
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "rpc/v1/amount.proto";
import "rpc/v1/ledger_objects.proto";
import "org/xrpl/rpc/v1/ledger_objects.proto";
import "org/xrpl/rpc/v1/common.proto";
message Meta {
// Next field: 5
message Meta
{
// index in ledger
uint64 transaction_index = 1;
uint64 transaction_index = 1 [jstype=JS_STRING];
// result code indicating whether the transaction succeeded or failed
TransactionResult transaction_result = 2;
repeated AffectedNode affected_nodes = 3;
CurrencyAmount delivered_amount = 4;
DeliveredAmount delivered_amount = 4;
}
message TransactionResult {
enum ResultType {
// Next field: 3
message TransactionResult
{
// Next field: 7
enum ResultType
{
RESULT_TYPE_UNSPECIFIED = 0;
// Claimed cost only
RESULT_TYPE_TEC = 1;
@@ -42,41 +49,42 @@ message TransactionResult {
string result = 2;
}
message AffectedNode {
// Next field: 6
message AffectedNode
{
LedgerEntryType ledger_entry_type = 1;
// 32 bytes
bytes ledger_index = 2;
oneof node {
oneof node
{
CreatedNode created_node = 3;
DeletedNode deleted_node = 4;
ModifiedNode modified_node = 5;
}
}
message CreatedNode {
// Next field: 2
message CreatedNode
{
LedgerObject new_fields = 1;
}
message DeletedNode {
// Next field: 2
message DeletedNode
{
LedgerObject final_fields = 1;
}
// Next field: 5
message ModifiedNode {
LedgerObject final_fields = 1;
LedgerObject previous_fields = 2;
// 32 bytes
bytes previous_transaction_id = 3;
uint32 previous_transaction_ledger_sequence = 4;
PreviousTransactionID previous_transaction_id = 3;
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 4;
}

View File

@@ -1,11 +1,15 @@
syntax = "proto3";
package rpc.v1;
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "rpc/v1/meta.proto";
import "org/xrpl/rpc/v1/meta.proto";
// A request to submit the signed transaction to the ledger.
message SubmitTransactionRequest {
// Next field: 3
message SubmitTransactionRequest
{
// The signed transaction to submit.
bytes signed_transaction = 1;
@@ -13,11 +17,14 @@ message SubmitTransactionRequest {
}
// A response when a signed transaction is submitted to the ledger.
message SubmitTransactionResponse {
// Next field: 5
message SubmitTransactionResponse
{
// Code indicating the preliminary result of the transaction.
TransactionResult engine_result = 1;
// Numeric code indicating the preliminary result of the transaction, directly correlated to engine_result.
// Numeric code indicating the preliminary result of the transaction,
// directly correlated to engine_result.
int64 engine_result_code = 2;
// Human-readable explanation of the transaction's preliminary result.

View File

@@ -0,0 +1,319 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/common.proto";
import "org/xrpl/rpc/v1/amount.proto";
import "org/xrpl/rpc/v1/account.proto";
// A message encompassing all transaction types
// Next field: 30
message Transaction
{
Account account = 1;
XRPDropsAmount fee = 2;
Sequence sequence = 3;
// Data specific to the type of transaction
oneof transaction_data
{
Payment payment = 4;
AccountSet account_set = 13;
AccountDelete account_delete = 14;
CheckCancel check_cancel = 15;
CheckCash check_cash = 16;
CheckCreate check_create = 17;
DepositPreauth deposit_preauth = 18;
EscrowCancel escrow_cancel = 19;
EscrowCreate escrow_create = 20;
EscrowFinish escrow_finish = 21;
OfferCancel offer_cancel = 22;
OfferCreate offer_create = 23;
PaymentChannelClaim payment_channel_claim = 24;
PaymentChannelCreate payment_channel_create= 25;
PaymentChannelFund payment_channel_fund = 26;
SetRegularKey set_regular_key = 27;
SignerListSet signer_list_set = 28;
TrustSet trust_set = 29;
}
SigningPublicKey signing_public_key = 5;
TransactionSignature transaction_signature = 6;
Flags flags = 7;
LastLedgerSequence last_ledger_sequence = 8;
SourceTag source_tag = 9;
repeated Memo memos = 10;
repeated Signer signers = 11;
AccountTransactionID account_transaction_id = 12;
}
// Next field: 4
message Memo
{
MemoData memo_data = 1;
MemoFormat memo_format = 2;
MemoType memo_type = 3;
}
// Next field: 4
message Signer
{
Account account = 1;
TransactionSignature transaction_signature = 2;
SigningPublicKey signing_public_key = 3;
}
// Next field: 8
message AccountSet
{
ClearFlag clear_flag = 1;
Domain domain = 2;
EmailHash email_hash = 3;
MessageKey message_key = 4;
SetFlag set_flag = 5;
TransferRate transfer_rate = 6;
TickSize tick_size = 7;
}
// Next field: 3
message AccountDelete
{
Destination destination = 1;
DestinationTag destination_tag = 2;
}
// Next field: 2
message CheckCancel
{
CheckID check_id = 1;
}
// Next field: 4
message CheckCash
{
CheckID check_id = 1;
oneof amount_oneof
{
Amount amount = 2;
DeliverMin deliver_min = 3;
}
}
// Next field: 6
message CheckCreate
{
Destination destination = 1;
SendMax send_max = 2;
DestinationTag destination_tag = 3;
Expiration expiration = 4;
InvoiceID invoice_id = 5;
}
// Next field: 3
message DepositPreauth
{
oneof authorization_oneof
{
Authorize authorize = 1;
Unauthorize unauthorize = 2;
}
}
// Next field: 3
message EscrowCancel
{
Owner owner = 1;
OfferSequence offer_sequence = 2;
}
// Next field: 7
message EscrowCreate
{
Amount amount = 1;
Destination destination = 2;
CancelAfter cancel_after = 3;
FinishAfter finish_after = 4;
Condition condition = 5;
DestinationTag destination_tag = 6;
}
// Next field: 5
message EscrowFinish
{
Owner owner = 1;
OfferSequence offer_sequence = 2;
Condition condition = 3;
Fulfillment fulfillment = 4;
}
// Next field: 2
message OfferCancel
{
OfferSequence offer_sequence = 1;
}
// Next field: 5
message OfferCreate
{
Expiration expiration = 1;
OfferSequence offer_sequence = 2;
TakerGets taker_gets = 3;
TakerPays taker_pays = 4;
}
// Next field: 8
message Payment
{
// Next field: 4
message PathElement
{
AccountAddress account = 1;
Currency currency = 2;
AccountAddress issuer = 3;
}
// Next field: 2
message Path
{
repeated PathElement elements = 1;
}
Amount amount = 1;
Destination destination = 2;
DestinationTag destination_tag = 3;
InvoiceID invoice_id = 4;
repeated Path paths = 5;
SendMax send_max = 6;
DeliverMin deliver_min = 7;
}
// Next field: 6
message PaymentChannelClaim
{
Channel channel = 1;
Balance balance = 2;
Amount amount = 3;
PaymentChannelSignature payment_channel_signature = 4;
PublicKey public_key = 5;
}
// Next field: 7
message PaymentChannelCreate
{
Amount amount = 1;
Destination destination = 2;
SettleDelay settle_delay = 3;
PublicKey public_key = 4;
CancelAfter cancel_after = 5;
DestinationTag destination_tag = 6;
}
// Next field: 4
message PaymentChannelFund
{
Channel channel = 1;
Amount amount = 2;
Expiration expiration = 3;
}
// Next field: 2
message SetRegularKey
{
RegularKey regular_key = 1;
}
// Next field: 3
message SignerListSet
{
SignerQuorum signer_quorum = 1;
repeated SignerEntry signer_entries = 2;
}
// Next field: 4
message TrustSet
{
LimitAmount limit_amount = 1;
QualityIn quality_in = 2;
QualityOut quality_out = 3;
}

View File

@@ -0,0 +1,30 @@
syntax = "proto3";
package org.xrpl.rpc.v1;
option java_package = "org.xrpl.rpc.v1";
option java_multiple_files = true;
import "org/xrpl/rpc/v1/get_account_info.proto";
import "org/xrpl/rpc/v1/get_fee.proto";
import "org/xrpl/rpc/v1/submit.proto";
import "org/xrpl/rpc/v1/get_transaction.proto";
import "org/xrpl/rpc/v1/get_account_transaction_history.proto";
// RPCs available to interact with the XRP Ledger.
service XRPLedgerAPIService {
// Get account info for an account on the XRP Ledger.
rpc GetAccountInfo (GetAccountInfoRequest) returns (GetAccountInfoResponse);
// Get the fee for a transaction on the XRP Ledger.
rpc GetFee (GetFeeRequest) returns (GetFeeResponse);
// Submit a signed transaction to the XRP Ledger.
rpc SubmitTransaction (SubmitTransactionRequest) returns (SubmitTransactionResponse);
// Get the status of a transaction
rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse);
rpc GetAccountTransactionHistory(GetAccountTransactionHistoryRequest) returns (GetAccountTransactionHistoryResponse);
}

View File

@@ -1,51 +0,0 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
// A request for the current transaction fee on the ledger.
message GetFeeRequest {
}
// Response to a GetFee RPC
message GetFeeResponse {
uint64 current_ledger_size = 1;
uint64 current_queue_size = 2;
Fee drops = 3;
uint64 expected_ledger_size = 4;
uint32 ledger_current_index = 5;
FeeLevels levels = 6;
uint64 max_queue_size = 7;
}
message Fee {
XRPDropsAmount base_fee = 1;
XRPDropsAmount median_fee = 2;
XRPDropsAmount minimum_fee = 3;
XRPDropsAmount open_ledger_fee = 4;
}
message FeeLevels {
uint64 median_level = 1;
uint64 minimum_level = 2;
uint64 open_ledger_level = 3;
uint64 reference_level = 4;
}

View File

@@ -1,175 +0,0 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
message LedgerObject {
oneof object {
AccountRoot account_root = 1;
RippleState ripple_state = 2;
Offer offer = 3;
SignerList signer_list = 4;
DirectoryNode directory_node = 5;
}
}
enum LedgerEntryType {
LEDGER_ENTRY_TYPE_UNSPECIFIED = 0;
LEDGER_ENTRY_TYPE_ACCOUNT_ROOT = 1;
LEDGER_ENTRY_TYPE_AMENDMENTS = 2;
LEDGER_ENTRY_TYPE_CHECK = 3;
LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH = 4;
LEDGER_ENTRY_TYPE_DIRECTORY_NODE = 5;
LEDGER_ENTRY_TYPE_ESCROW = 6;
LEDGER_ENTRY_TYPE_FEE_SETTINGS = 7;
LEDGER_ENTRY_TYPE_LEDGER_HASHES = 8;
LEDGER_ENTRY_TYPE_OFFER = 9;
LEDGER_ENTRY_TYPE_PAY_CHANNEL = 10;
LEDGER_ENTRY_TYPE_RIPPLE_STATE = 11;
LEDGER_ENTRY_TYPE_SIGNER_LIST = 12;
}
message DirectoryNode {
uint32 flags = 1;
// 32 bytes
bytes root_index = 2;
repeated bytes indexes = 3;
uint64 index_next = 4;
uint64 index_previous = 5;
string owner = 6;
Currency taker_pays_currency = 7;
// 20 bytes
bytes taker_pays_issuer = 8;
Currency taker_gets_currency = 9;
// 20 bytes
bytes taker_gets_issuer = 10;
}
message SignerList {
uint32 flags = 1;
// 32 bytes
bytes previous_txn_id = 2;
uint32 previous_transaction_ledger_sequence = 3;
uint64 owner_node = 4;
repeated SignerEntry signer_entries = 5;
uint32 signer_list_id = 6;
uint32 signer_quorum = 7;
}
message SignerEntry {
AccountAddress account = 1;
// this is actually uint16, but protobuf can't express uint16
uint32 signer_weight = 2;
}
message AccountRoot {
AccountAddress account = 1;
XRPDropsAmount balance = 2;
uint32 sequence = 3;
uint32 flags = 4;
uint32 owner_count = 5;
// 32 bytes
bytes previous_transaction_id = 6;
uint32 previous_transaction_ledger_sequence = 7;
// 32 bytes
bytes account_transaction_id = 8;
// Variable length
bytes domain = 9;
// 16 bytes
bytes email_hash = 10;
// Variable length
bytes message_key = 11;
// base58 encoding
string regular_key = 12;
uint32 tick_size = 13;
uint32 transfer_rate = 14;
}
message RippleState {
CurrencyAmount balance = 1;
uint32 flags = 2;
CurrencyAmount low_limit = 3;
CurrencyAmount high_limit = 4;
uint64 low_node = 5;
uint64 high_node = 6;
uint32 low_quality_in = 7;
uint32 low_quality_out = 8;
uint32 high_quality_in = 9;
uint32 high_quality_out = 10;
// 32 bytes
bytes previous_transaction_id = 11;
uint32 previous_transaction_ledger_sequence = 12;
}
message Offer {
string account = 1;
uint32 sequence = 2;
uint32 flags = 3;
CurrencyAmount taker_pays = 4;
CurrencyAmount taker_gets = 5;
bytes book_directory = 6;
uint64 book_node = 7;
uint64 owner_node = 8;
uint32 expiration = 9;
// 32 bytes
bytes previous_transaction_id = 10;
uint32 previous_transaction_ledger_sequence = 11;
}

View File

@@ -1,96 +0,0 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/amount.proto";
// A class encompassing all transactions.
message Transaction {
// The account originating the transaction.
AccountAddress account = 1;
// The fee attached to the transaction.
XRPDropsAmount fee = 2;
// The sequence number for the transaction.
uint32 sequence = 3;
// Data specific to a the type of transaction being submitted.
oneof transaction_data {
Payment payment = 4;
}
// Public key of the account which signed the transaction. Variable length
bytes signing_public_key = 5;
// Variable length
bytes signature = 6;
uint32 flags = 7;
uint32 last_ledger_sequence = 8;
uint32 source_tag = 9;
repeated Memo memos = 10;
repeated Signer signers = 11;
bytes account_transaction_id = 12;
}
message Memo {
// Variable length
bytes memo_data = 1;
// Variable length
bytes memo_format = 2;
// Variable length
bytes memo_type = 3;
}
message Signer {
AccountAddress account = 1;
// Variable length
bytes transaction_signature = 2;
// Variable length
bytes signing_public_key = 3;
}
message Payment {
// The amount of currency to pay, in either issued currency or XRP.
CurrencyAmount amount = 1;
// The destination of the payment.
AccountAddress destination = 2;
uint32 destination_tag = 3;
// 32 bytes
bytes invoice_id = 4;
repeated Path paths = 5;
CurrencyAmount send_max = 6;
CurrencyAmount deliver_min = 7;
}
message Path {
repeated PathElement elements = 1;
}
message PathElement {
AccountAddress account = 1;
Currency currency = 2;
AccountAddress issuer = 3;
}

View File

@@ -1,40 +0,0 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/transaction.proto";
import "rpc/v1/meta.proto";
message GetTxRequest {
// hash of the transaction. 32 bytes
bytes hash = 1;
// if true, return data in binary format
bool binary = 2;
}
message GetTxResponse {
// The actual transaction
oneof serialized_transaction {
Transaction transaction = 1;
// Variable length
bytes transaction_binary = 2;
};
// Sequence number of ledger that contains this transaction
uint32 ledger_index = 3;
// 32 bytes
bytes hash = 4;
// whether the ledger has been validated
bool validated = 5;
// metadata about the transaction
oneof serialized_meta {
Meta meta = 6;
// Variable length
bytes meta_binary = 7;
}
}

View File

@@ -1,25 +0,0 @@
syntax = "proto3";
package rpc.v1;
import "rpc/v1/account_info.proto";
import "rpc/v1/fee.proto";
import "rpc/v1/submit.proto";
import "rpc/v1/tx.proto";
// RPCs available to interact with the XRP Ledger.
service XRPLedgerAPIService {
// Get account info for an account on the XRP Ledger.
rpc GetAccountInfo (GetAccountInfoRequest) returns (GetAccountInfoResponse);
// Get the fee for a transaction on the XRP Ledger.
rpc GetFee (GetFeeRequest) returns (GetFeeResponse);
// Submit a signed transaction to the XRP Ledger.
rpc SubmitTransaction (SubmitTransactionRequest) returns (SubmitTransactionResponse);
// Get the status of a transaction
rpc GetTx(GetTxRequest) returns (GetTxResponse);
}

View File

@@ -20,8 +20,12 @@
#ifndef RIPPLE_RPC_DELIVEREDAMOUNT_H_INCLUDED
#define RIPPLE_RPC_DELIVEREDAMOUNT_H_INCLUDED
#include <org/xrpl/rpc/v1/amount.pb.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/Protocol.h>
#include <functional>
#include <memory>
#include <rpc/v1/amount.pb.h>
namespace Json {
class Value;
@@ -53,23 +57,22 @@ void
insertDeliveredAmount(
Json::Value& meta,
ReadView const&,
std::shared_ptr<STTx const> serializedTx,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const&);
void
insertDeliveredAmount(
Json::Value& meta,
JsonContext&,
std::shared_ptr<Transaction>,
TxMeta const&);
void
insertDeliveredAmount(
rpc::v1::CurrencyAmount& proto,
Context&,
std::shared_ptr<Transaction>,
RPC::JsonContext const&,
std::shared_ptr<Transaction> const&,
TxMeta const&);
std::optional<STAmount>
getDeliveredAmount(
RPC::Context const& context,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta,
LedgerIndex const& ledgerIndex);
/** @} */
} // RPC

View File

@@ -22,7 +22,7 @@
#include <ripple/rpc/Context.h>
#include <grpcpp/grpcpp.h>
#include <rpc/v1/xrp_ledger.pb.h>
#include <org/xrpl/rpc/v1/xrp_ledger.pb.h>
namespace ripple {
@@ -34,18 +34,22 @@ namespace ripple {
* the status will be sent to the client, and the response will be ommitted
*/
std::pair<rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context);
std::pair<org::xrpl::rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context);
std::pair<rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<rpc::v1::GetFeeRequest>& context);
std::pair<org::xrpl::rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetFeeRequest>& context);
std::pair<rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<rpc::v1::SubmitTransactionRequest>& context);
std::pair<org::xrpl::rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::SubmitTransactionRequest>& context);
// NOTE, this only supports Payment transactions at this time
std::pair<rpc::v1::GetTxResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<rpc::v1::GetTxRequest>& context);
std::pair<org::xrpl::rpc::v1::GetTransactionResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetTransactionRequest>& context);
std::pair<org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse, grpc::Status>
doAccountTxGrpc(
RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>& context);
} // namespace ripple

View File

@@ -106,7 +106,7 @@ public:
/** Apply the Status to a JsonObject
*/
template <class Object>
void inject (Object& object)
void inject (Object& object) const
{
if (auto ec = toErrorCode())
{

View File

@@ -28,6 +28,7 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/GRPCHelpers.h>
#include <grpc/status.h>
namespace ripple {
@@ -184,15 +185,15 @@ Json::Value doAccountInfo (RPC::JsonContext& context)
return result;
}
std::pair<rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
std::pair<org::xrpl::rpc::v1::GetAccountInfoResponse, grpc::Status>
doAccountInfoGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context)
{
// Return values
rpc::v1::GetAccountInfoResponse result;
org::xrpl::rpc::v1::GetAccountInfoResponse result;
grpc::Status status = grpc::Status::OK;
// input
rpc::v1::GetAccountInfoRequest& params = context.params;
org::xrpl::rpc::v1::GetAccountInfoRequest& params = context.params;
// get ledger
std::shared_ptr<ReadView const> ledger;
@@ -233,7 +234,7 @@ doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
auto const sleAccepted = ledger->read(keylet::account(accountID));
if (sleAccepted)
{
RPC::populateAccountRoot(*result.mutable_account_data(), *sleAccepted);
RPC::convert(*result.mutable_account_data(), *sleAccepted);
// signer lists
if (params.signer_lists())
@@ -241,9 +242,9 @@ doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
auto const sleSigners = ledger->read(keylet::signers(accountID));
if (sleSigners)
{
rpc::v1::SignerList& signerListProto =
org::xrpl::rpc::v1::SignerList& signerListProto =
*result.mutable_signer_list();
RPC::populateSignerList(signerListProto, *sleSigners);
RPC::convert(signerListProto, *sleSigners);
}
}
@@ -259,8 +260,8 @@ doAccountInfoGrpc(RPC::GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
}
auto const txs =
context.app.getTxQ().getAccountTxs(accountID, *ledger);
rpc::v1::QueueData& queueData = *result.mutable_queue_data();
RPC::populateQueueData(queueData, txs);
org::xrpl::rpc::v1::QueueData& queueData = *result.mutable_queue_data();
RPC::convert(queueData, txs);
}
}
else

View File

@@ -25,16 +25,481 @@
#include <ripple/ledger/ReadView.h>
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/jss.h>
#include <ripple/protocol/UintTypes.h>
#include <ripple/protocol/jss.h>
#include <ripple/resource/Fees.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/GRPCHelpers.h>
#include <grpcpp/grpcpp.h>
namespace ripple {
using LedgerSequence = uint32_t;
using LedgerHash = uint256;
using LedgerShortcut = RPC::LedgerShortcut;
using AccountTxMarker = NetworkOPs::AccountTxMarker;
struct LedgerRange
{
uint32_t min;
uint32_t max;
};
using LedgerSpecifier =
std::variant<LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash>;
struct AccountTxArgs
{
AccountID account;
std::optional<LedgerSpecifier> ledger;
bool binary = false;
bool forward = false;
uint32_t limit = 0;
std::optional<AccountTxMarker> marker;
};
using TxnsData = NetworkOPs::AccountTxs;
using TxnsDataBinary = NetworkOPs::MetaTxsList;
using TxnDataBinary = NetworkOPs::txnMetaLedgerType;
struct AccountTxResult
{
std::variant<TxnsData, TxnsDataBinary> transactions;
LedgerRange ledgerRange;
uint32_t limit;
std::optional<AccountTxMarker> marker;
};
// parses args into a ledger specifier, or returns a grpc status object on error
std::variant<std::optional<LedgerSpecifier>, grpc::Status>
parseLedgerArgs(
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest const& params)
{
grpc::Status status;
if (params.has_ledger_range())
{
uint32_t min = params.ledger_range().ledger_index_min();
uint32_t max = params.ledger_range().ledger_index_max();
// if min is set but not max, need to set max
if (min != 0 && max == 0)
{
max = UINT32_MAX;
}
return LedgerRange{min, max};
}
else if (params.has_ledger_specifier())
{
LedgerSpecifier ledger;
auto& specifier = params.ledger_specifier();
using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
LedgerCase ledgerCase = specifier.ledger_case();
if (ledgerCase == LedgerCase::kShortcut)
{
using LedgerSpecifier = org::xrpl::rpc::v1::LedgerSpecifier;
if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_VALIDATED)
ledger = LedgerShortcut::VALIDATED;
else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CLOSED)
ledger = LedgerShortcut::CLOSED;
else if (specifier.shortcut() == LedgerSpecifier::SHORTCUT_CURRENT)
ledger = LedgerShortcut::CURRENT;
else
return {};
}
else if (ledgerCase == LedgerCase::kSequence)
{
ledger = specifier.sequence();
}
else if (ledgerCase == LedgerCase::kHash)
{
if (uint256::size() != specifier.hash().size())
{
grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
"ledger hash malformed"};
return errorStatus;
}
ledger = uint256::fromVoid(specifier.hash().data());
}
return ledger;
}
return std::optional<LedgerSpecifier>{};
}
// parses args into a ledger specifier, or returns a Json object on error
std::variant<std::optional<LedgerSpecifier>, Json::Value>
parseLedgerArgs(Json::Value const& params)
{
Json::Value response;
if (params.isMember(jss::ledger_index_min) ||
params.isMember(jss::ledger_index_max))
{
uint32_t min = params.isMember(jss::ledger_index_min) &&
params[jss::ledger_index_min].asInt() >= 0
? params[jss::ledger_index_min].asUInt()
: 0;
uint32_t max = params.isMember(jss::ledger_index_max) &&
params[jss::ledger_index_max].asInt() >= 0
? params[jss::ledger_index_max].asUInt()
: UINT32_MAX;
return LedgerRange{min, max};
}
else if (params.isMember(jss::ledger_hash))
{
auto& hashValue = params[jss::ledger_hash];
if (!hashValue.isString())
{
RPC::Status status{rpcINVALID_PARAMS, "ledgerHashNotString"};
status.inject(response);
return response;
}
LedgerHash hash;
if (!hash.SetHex(hashValue.asString()))
{
RPC::Status status{rpcINVALID_PARAMS, "ledgerHashMalformed"};
status.inject(response);
return response;
}
return hash;
}
else if (params.isMember(jss::ledger_index))
{
LedgerSpecifier ledger;
if (params[jss::ledger_index].isNumeric())
ledger = params[jss::ledger_index].asInt();
else
{
std::string ledgerStr = params[jss::ledger_index].asString();
if (ledgerStr == "current" || ledgerStr.empty())
ledger = LedgerShortcut::CURRENT;
else if (ledgerStr == "closed")
ledger = LedgerShortcut::CLOSED;
else if (ledgerStr == "validated")
ledger = LedgerShortcut::VALIDATED;
else
{
RPC::Status status{rpcINVALID_PARAMS,
"ledger_index string malformed"};
status.inject(response);
return response;
}
}
return ledger;
}
return std::optional<LedgerSpecifier>{};
}
std::variant<LedgerRange, RPC::Status>
getLedgerRange(
RPC::Context& context,
std::optional<LedgerSpecifier> const& ledgerSpecifier)
{
std::uint32_t uValidatedMin;
std::uint32_t uValidatedMax;
bool bValidated =
context.ledgerMaster.getValidatedRange(uValidatedMin, uValidatedMax);
if (!bValidated)
{
// Don't have a validated ledger range.
return rpcLGR_IDXS_INVALID;
}
std::uint32_t uLedgerMin = uValidatedMin;
std::uint32_t uLedgerMax = uValidatedMax;
// Does request specify a ledger or ledger range?
if (ledgerSpecifier)
{
auto const status = std::visit(
[&](auto const& ls) -> RPC::Status {
using T = std::decay_t<decltype(ls)>;
if constexpr (std::is_same_v<T, LedgerRange>)
{
if (ls.min > uValidatedMin)
{
uLedgerMin = ls.min;
}
if (ls.max < uValidatedMax)
{
uLedgerMax = ls.max;
}
if (uLedgerMax < uLedgerMin)
return rpcLGR_IDXS_INVALID;
}
else
{
std::shared_ptr<ReadView const> ledgerView;
auto const status = getLedger(ledgerView, ls, context);
if (!ledgerView)
{
return status;
}
bool validated = RPC::isValidated(
context.ledgerMaster, *ledgerView, context.app);
if (!validated || ledgerView->info().seq > uValidatedMax ||
ledgerView->info().seq < uValidatedMin)
{
return rpcLGR_NOT_VALIDATED;
}
uLedgerMin = uLedgerMax = ledgerView->info().seq;
}
return RPC::Status::OK;
},
*ledgerSpecifier);
if (status)
return status;
}
return LedgerRange{uLedgerMin, uLedgerMax};
}
std::pair<AccountTxResult, RPC::Status>
doAccountTxHelp(RPC::Context& context, AccountTxArgs const& args)
{
AccountTxResult result;
context.loadType = Resource::feeMediumBurdenRPC;
auto lgrRange = getLedgerRange(context, args.ledger);
if (auto stat = std::get_if<RPC::Status>(&lgrRange))
{
// An error occurred getting the requested ledger range
return {result, *stat};
}
result.ledgerRange = std::get<LedgerRange>(lgrRange);
result.marker = args.marker;
if (args.binary)
{
result.transactions = context.netOps.getTxsAccountB(
args.account,
result.ledgerRange.min,
result.ledgerRange.max,
args.forward,
result.marker,
args.limit,
isUnlimited(context.role));
}
else
{
result.transactions = context.netOps.getTxsAccount(
args.account,
result.ledgerRange.min,
result.ledgerRange.max,
args.forward,
result.marker,
args.limit,
isUnlimited(context.role));
}
result.limit = args.limit;
return {result, rpcSUCCESS};
}
std::pair<
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
grpc::Status>
populateProtoResponse(
std::pair<AccountTxResult, RPC::Status> const& res,
AccountTxArgs const& args,
RPC::GRPCContext<
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest> const& context)
{
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
grpc::Status status = grpc::Status::OK;
RPC::Status const& error = res.second;
if (error.toErrorCode() != rpcSUCCESS)
{
if (error.toErrorCode() == rpcLGR_NOT_FOUND)
{
status = {grpc::StatusCode::NOT_FOUND, error.message()};
}
else
{
status = {grpc::StatusCode::INVALID_ARGUMENT, error.message()};
}
}
else
{
AccountTxResult const& result = res.first;
// account_tx always returns validated data
response.set_validated(true);
response.set_limit(result.limit);
response.mutable_account()->set_address(
context.params.account().address());
response.set_ledger_index_min(result.ledgerRange.min);
response.set_ledger_index_max(result.ledgerRange.max);
if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
{
assert(!args.binary);
for (auto const& [txn, txnMeta] : *txnsData)
{
if (txn)
{
auto txnProto = response.add_transactions();
RPC::convert(
*txnProto->mutable_transaction(),
txn->getSTransaction());
// account_tx always returns validated data
txnProto->set_validated(true);
txnProto->set_ledger_index(txn->getLedger());
auto& hash = txn->getID();
txnProto->set_hash(hash.data(), hash.size());
auto closeTime =
context.app.getLedgerMaster().getCloseTimeBySeq(
txn->getLedger());
if (closeTime)
txnProto->mutable_date()->set_value(
closeTime->time_since_epoch().count());
if (txnMeta)
{
if (!txnMeta->hasDeliveredAmount())
{
std::optional<STAmount> amount = getDeliveredAmount(
context,
txn->getSTransaction(),
*txnMeta,
txn->getLedger());
if (amount)
{
txnMeta->setDeliveredAmount(*amount);
}
}
RPC::convert(*txnProto->mutable_meta(), txnMeta);
}
}
}
}
else
{
assert(args.binary);
for (auto const& binaryData :
std::get<TxnsDataBinary>(result.transactions))
{
auto txnProto = response.add_transactions();
Blob const& txnBlob = std::get<0>(binaryData);
txnProto->set_transaction_binary(
txnBlob.data(), txnBlob.size());
Blob const& metaBlob = std::get<1>(binaryData);
txnProto->set_meta_binary(metaBlob.data(), metaBlob.size());
txnProto->set_ledger_index(std::get<2>(binaryData));
// account_tx always returns validated data
txnProto->set_validated(true);
auto closeTime =
context.app.getLedgerMaster().getCloseTimeBySeq(
std::get<2>(binaryData));
if (closeTime)
txnProto->mutable_date()->set_value(
closeTime->time_since_epoch().count());
}
}
if (result.marker)
{
response.mutable_marker()->set_ledger_index(
result.marker->ledgerSeq);
response.mutable_marker()->set_account_sequence(
result.marker->txnSeq);
}
}
return {response, status};
}
Json::Value
populateJsonResponse(
std::pair<AccountTxResult, RPC::Status> const& res,
AccountTxArgs const& args,
RPC::JsonContext const& context)
{
Json::Value response;
RPC::Status const& error = res.second;
if (error.toErrorCode() != rpcSUCCESS)
{
error.inject(response);
}
else
{
AccountTxResult const& result = res.first;
response[jss::validated] = true;
response[jss::limit] = result.limit;
response[jss::account] = context.params[jss::account].asString();
response[jss::ledger_index_min] = result.ledgerRange.min;
response[jss::ledger_index_max] = result.ledgerRange.max;
Json::Value& jvTxns = (response[jss::transactions] = Json::arrayValue);
if (auto txnsData = std::get_if<TxnsData>(&result.transactions))
{
assert(!args.binary);
for (auto const& [txn, txnMeta] : *txnsData)
{
if (txn)
{
Json::Value& jvObj = jvTxns.append(Json::objectValue);
jvObj[jss::tx] = txn->getJson(JsonOptions::include_date);
if (txnMeta)
{
jvObj[jss::meta] =
txnMeta->getJson(JsonOptions::include_date);
jvObj[jss::validated] = true;
insertDeliveredAmount(
jvObj[jss::meta], context, txn, *txnMeta);
}
}
}
}
else
{
assert(args.binary);
for (auto const& binaryData :
std::get<TxnsDataBinary>(result.transactions))
{
Json::Value& jvObj = jvTxns.append(Json::objectValue);
jvObj[jss::tx_blob] = strHex(std::get<0>(binaryData));
jvObj[jss::meta] = strHex(std::get<1>(binaryData));
jvObj[jss::ledger_index] = std::get<2>(binaryData);
jvObj[jss::validated] = true;
}
}
if (result.marker)
{
response[jss::marker] = Json::objectValue;
response[jss::marker][jss::ledger] = result.marker->ledgerSeq;
response[jss::marker][jss::seq] = result.marker->txnSeq;
}
}
return response;
}
// {
// account: account,
// ledger_index_min: ledger_index // optional, defaults to earliest
@@ -42,161 +507,107 @@ namespace ripple {
// binary: boolean, // optional, defaults to false
// forward: boolean, // optional, defaults to false
// limit: integer, // optional
// marker: opaque // optional, resume previous query
// marker: object {ledger: ledger_index, seq: txn_sequence} // optional,
// resume previous query
// }
Json::Value doAccountTx (RPC::JsonContext& context)
Json::Value
doAccountTxJson(RPC::JsonContext& context)
{
auto& params = context.params;
AccountTxArgs args;
Json::Value response;
int limit = params.isMember (jss::limit) ?
params[jss::limit].asUInt () : -1;
bool bBinary = params.isMember (jss::binary) && params[jss::binary].asBool ();
bool bForward = params.isMember (jss::forward) && params[jss::forward].asBool ();
std::uint32_t uLedgerMin;
std::uint32_t uLedgerMax;
std::uint32_t uValidatedMin;
std::uint32_t uValidatedMax;
bool bValidated = context.ledgerMaster.getValidatedRange (
uValidatedMin, uValidatedMax);
args.limit = params.isMember(jss::limit) ? params[jss::limit].asUInt() : 0;
args.binary = params.isMember(jss::binary) && params[jss::binary].asBool();
args.forward =
params.isMember(jss::forward) && params[jss::forward].asBool();
if (!bValidated)
if (!params.isMember(jss::account))
return rpcError(rpcINVALID_PARAMS);
auto const account =
parseBase58<AccountID>(params[jss::account].asString());
if (!account)
return rpcError(rpcACT_MALFORMED);
args.account = *account;
auto parseRes = parseLedgerArgs(params);
if (auto jv = std::get_if<Json::Value>(&parseRes))
{
// Don't have a validated ledger range.
return rpcError (rpcLGR_IDXS_INVALID);
}
if (!params.isMember (jss::account))
return rpcError (rpcINVALID_PARAMS);
auto const account = parseBase58<AccountID>(
params[jss::account].asString());
if (! account)
return rpcError (rpcACT_MALFORMED);
context.loadType = Resource::feeMediumBurdenRPC;
if (params.isMember (jss::ledger_index_min) ||
params.isMember (jss::ledger_index_max))
{
std::int64_t iLedgerMin = params.isMember (jss::ledger_index_min)
? params[jss::ledger_index_min].asInt () : -1;
std::int64_t iLedgerMax = params.isMember (jss::ledger_index_max)
? params[jss::ledger_index_max].asInt () : -1;
uLedgerMin = iLedgerMin == -1 ? uValidatedMin :
((iLedgerMin >= uValidatedMin) ? iLedgerMin : uValidatedMin);
uLedgerMax = iLedgerMax == -1 ? uValidatedMax :
((iLedgerMax <= uValidatedMax) ? iLedgerMax : uValidatedMax);
if (uLedgerMax < uLedgerMin)
return rpcError (rpcLGR_IDXS_INVALID);
}
else if(params.isMember (jss::ledger_hash) ||
params.isMember (jss::ledger_index))
{
std::shared_ptr<ReadView const> ledger;
auto ret = RPC::lookupLedger (ledger, context);
if (! ledger)
return ret;
if (! ret[jss::validated].asBool() ||
(ledger->info().seq > uValidatedMax) ||
(ledger->info().seq < uValidatedMin))
{
return rpcError (rpcLGR_NOT_VALIDATED);
}
uLedgerMin = uLedgerMax = ledger->info().seq;
return *jv;
}
else
{
uLedgerMin = uValidatedMin;
uLedgerMax = uValidatedMax;
args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
}
Json::Value resumeToken;
if (params.isMember(jss::marker))
resumeToken = params[jss::marker];
#ifndef DEBUG
try
{
#endif
Json::Value ret (Json::objectValue);
ret[jss::account] = context.app.accountIDCache().toBase58(*account);
Json::Value& jvTxns = (ret[jss::transactions] = Json::arrayValue);
if (bBinary)
auto& token = params[jss::marker];
if (!token.isMember(jss::ledger) || !token.isMember(jss::seq) ||
!token[jss::ledger].isConvertibleTo(Json::ValueType::uintValue) ||
!token[jss::seq].isConvertibleTo(Json::ValueType::uintValue))
{
auto txns = context.netOps.getTxsAccountB (
*account, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
isUnlimited (context.role));
for (auto& it: txns)
{
Json::Value& jvObj = jvTxns.append (Json::objectValue);
jvObj[jss::tx_blob] = std::get<0> (it);
jvObj[jss::meta] = std::get<1> (it);
std::uint32_t uLedgerIndex = std::get<2> (it);
jvObj[jss::ledger_index] = uLedgerIndex;
jvObj[jss::validated] = bValidated &&
uValidatedMin <= uLedgerIndex &&
uValidatedMax >= uLedgerIndex;
}
RPC::Status status{
rpcINVALID_PARAMS,
"invalid marker. Provide ledger index via ledger field, and "
"transaction sequence number via seq field"};
status.inject(response);
return response;
}
else
{
auto txns = context.netOps.getTxsAccount (
*account, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
isUnlimited (context.role));
for (auto const& [txn, txMeta]: txns)
{
Json::Value& jvObj = jvTxns.append (Json::objectValue);
if (txn)
jvObj[jss::tx] =
txn->getJson (JsonOptions::include_date);
if (txMeta)
{
auto metaJ = txMeta->getJson (JsonOptions::include_date);
insertDeliveredAmount (metaJ, context, txn, *txMeta);
jvObj[jss::meta] = std::move(metaJ);
std::uint32_t uLedgerIndex = txMeta->getLgrSeq ();
jvObj[jss::validated] = bValidated &&
uValidatedMin <= uLedgerIndex &&
uValidatedMax >= uLedgerIndex;
}
}
}
//Add information about the original query
ret[jss::ledger_index_min] = uLedgerMin;
ret[jss::ledger_index_max] = uLedgerMax;
if (params.isMember (jss::limit))
ret[jss::limit] = limit;
if (resumeToken)
ret[jss::marker] = resumeToken;
return ret;
#ifndef DEBUG
}
catch (std::exception const&)
{
return rpcError (rpcINTERNAL);
args.marker = {token[jss::ledger].asUInt(), token[jss::seq].asUInt()};
}
#endif
auto res = doAccountTxHelp(context, args);
return populateJsonResponse(res, args, context);
}
} // ripple
std::pair<
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
grpc::Status>
doAccountTxGrpc(
RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>&
context)
{
// return values
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse response;
grpc::Status status = grpc::Status::OK;
AccountTxArgs args;
auto& request = context.params;
auto const account = parseBase58<AccountID>(request.account().address());
if (!account)
{
return {
{},
{grpc::StatusCode::INVALID_ARGUMENT, "Could not decode account"}};
}
args.account = *account;
args.limit = request.limit();
args.binary = request.binary();
args.forward = request.forward();
if (request.has_marker())
{
args.marker = {request.marker().ledger_index(),
request.marker().account_sequence()};
}
auto parseRes = parseLedgerArgs(request);
if (auto stat = std::get_if<grpc::Status>(&parseRes))
{
return {response, *stat};
}
else
{
args.ledger = std::get<std::optional<LedgerSpecifier>>(parseRes);
}
auto res = doAccountTxHelp(context, args);
return populateProtoResponse(res, args, context);
}
} // namespace ripple

View File

@@ -155,8 +155,8 @@ Json::Value doAccountTxOld (RPC::JsonContext& context)
Json::Value& jvObj = jvTxns.append (Json::objectValue);
std::uint32_t uLedgerIndex = std::get<2> (*it);
jvObj[jss::tx_blob] = std::get<0> (*it);
jvObj[jss::meta] = std::get<1> (*it);
jvObj[jss::tx_blob] = strHex(std::get<0> (*it));
jvObj[jss::meta] = strHex(std::get<1> (*it));
jvObj[jss::ledger_index] = uLedgerIndex;
jvObj[jss::validated]
= bValidated

View File

@@ -26,7 +26,7 @@
namespace ripple {
Json::Value doAccountTxOld (RPC::JsonContext& context);
Json::Value doAccountTx (RPC::JsonContext& context);
Json::Value doAccountTxJson (RPC::JsonContext& context);
// Temporary switching code until the old account_tx is removed
Json::Value doAccountTxSwitch (RPC::JsonContext& context)
@@ -39,7 +39,7 @@ Json::Value doAccountTxSwitch (RPC::JsonContext& context)
{
return doAccountTxOld(context);
}
return doAccountTx(context);
return doAccountTxJson(context);
}
} // ripple

View File

@@ -38,10 +38,10 @@ namespace ripple
return context.params;
}
std::pair<rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<rpc::v1::GetFeeRequest>& context)
std::pair<org::xrpl::rpc::v1::GetFeeResponse, grpc::Status>
doFeeGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetFeeRequest>& context)
{
rpc::v1::GetFeeResponse reply;
org::xrpl::rpc::v1::GetFeeResponse reply;
grpc::Status status = grpc::Status::OK;
Application& app = context.app;
@@ -62,23 +62,23 @@ doFeeGrpc(RPC::GRPCContext<rpc::v1::GetFeeRequest>& context)
reply.set_max_queue_size(*metrics.txQMaxSize);
// fee levels data
rpc::v1::FeeLevels& levels = *reply.mutable_levels();
org::xrpl::rpc::v1::FeeLevels& levels = *reply.mutable_levels();
levels.set_median_level(metrics.medFeeLevel.fee());
levels.set_minimum_level(metrics.minProcessingFeeLevel.fee());
levels.set_open_ledger_level(metrics.openLedgerFeeLevel.fee());
levels.set_reference_level(metrics.referenceFeeLevel.fee());
// fee data
rpc::v1::Fee& drops = *reply.mutable_drops();
org::xrpl::rpc::v1::Fee& fee = *reply.mutable_fee();
auto const baseFee = view->fees().base;
drops.mutable_base_fee()->set_drops(
fee.mutable_base_fee()->set_drops(
toDrops(metrics.referenceFeeLevel, baseFee).second.drops());
drops.mutable_minimum_fee()->set_drops(
fee.mutable_minimum_fee()->set_drops(
toDrops(metrics.minProcessingFeeLevel, baseFee).second.drops());
drops.mutable_median_fee()->set_drops(
fee.mutable_median_fee()->set_drops(
toDrops(metrics.medFeeLevel, baseFee).second.drops());
drops.mutable_open_ledger_fee()->set_drops(
fee.mutable_open_ledger_fee()->set_drops(
(toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee).second +
1)
.drops());

View File

@@ -30,9 +30,9 @@ Json::Value doAccountLines (RPC::JsonContext&);
Json::Value doAccountChannels (RPC::JsonContext&);
Json::Value doAccountObjects (RPC::JsonContext&);
Json::Value doAccountOffers (RPC::JsonContext&);
Json::Value doAccountTx (RPC::JsonContext&);
Json::Value doAccountTxSwitch (RPC::JsonContext&);
Json::Value doAccountTxOld (RPC::JsonContext&);
Json::Value doAccountTxJson (RPC::JsonContext&);
Json::Value doBookOffers (RPC::JsonContext&);
Json::Value doBlackList (RPC::JsonContext&);
Json::Value doCanDelete (RPC::JsonContext&);
@@ -79,7 +79,7 @@ Json::Value doSubmit (RPC::JsonContext&);
Json::Value doSubmitMultiSigned (RPC::JsonContext&);
Json::Value doSubscribe (RPC::JsonContext&);
Json::Value doTransactionEntry (RPC::JsonContext&);
Json::Value doTx (RPC::JsonContext&);
Json::Value doTxJson (RPC::JsonContext&);
Json::Value doTxHistory (RPC::JsonContext&);
Json::Value doUnlList (RPC::JsonContext&);
Json::Value doUnsubscribe (RPC::JsonContext&);

View File

@@ -28,6 +28,7 @@
#include <ripple/rpc/impl/TransactionSign.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/GRPCHelpers.h>
namespace ripple {
@@ -183,11 +184,11 @@ Json::Value doSubmit (RPC::JsonContext& context)
}
}
std::pair<rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<rpc::v1::SubmitTransactionRequest>& context)
std::pair<org::xrpl::rpc::v1::SubmitTransactionResponse, grpc::Status>
doSubmitGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::SubmitTransactionRequest>& context)
{
// return values
rpc::v1::SubmitTransactionResponse result;
org::xrpl::rpc::v1::SubmitTransactionResponse result;
grpc::Status status = grpc::Status::OK;
// input
@@ -261,8 +262,7 @@ doSubmitGrpc(RPC::GRPCContext<rpc::v1::SubmitTransactionRequest>& context)
// return preliminary result
if (temUNCERTAIN != tpTrans->getResult())
{
RPC::populateTransactionResultType(
*result.mutable_engine_result(), tpTrans->getResult());
RPC::convert(*result.mutable_engine_result(), tpTrans->getResult());
std::string sToken;
std::string sHuman;

View File

@@ -27,7 +27,9 @@
#include <ripple/rpc/Context.h>
#include <ripple/rpc/DeliveredAmount.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/GRPCHelpers.h>
#include <ripple/rpc/GRPCHandlers.h>
#include <ripple/basics/ToString.h>
namespace ripple {
@@ -64,13 +66,6 @@ isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash)
return ledgerMaster.getHashBySeq (seq) == hash;
}
static
bool
isValidated (RPC::JsonContext& context, std::uint32_t seq, uint256 const& hash)
{
return isValidated(context.ledgerMaster, seq, hash);
}
bool
getMetaHex (Ledger const& ledger,
uint256 const& transID, std::string& hex)
@@ -91,206 +86,103 @@ getMetaHex (Ledger const& ledger,
return true;
}
Json::Value doTx (RPC::JsonContext& context)
enum class SearchedAll { no, yes, unknown };
struct TxResult
{
if (!context.params.isMember (jss::transaction))
return rpcError (rpcINVALID_PARAMS);
Transaction::pointer txn;
std::variant<std::shared_ptr<TxMeta>, Blob> meta;
bool validated = false;
SearchedAll searchedAll;
};
bool binary = context.params.isMember (jss::binary)
&& context.params[jss::binary].asBool ();
struct TxArgs
{
uint256 hash;
bool binary = false;
std::optional<std::pair<uint32_t,uint32_t>> ledgerRange;
};
auto const txid = context.params[jss::transaction].asString ();
if (!isHexTxID (txid))
return rpcError (rpcNOT_IMPL);
std::pair<TxResult, RPC::Status>
doTxHelp(RPC::Context& context, TxArgs const& args)
{
TxResult result;
ClosedInterval<uint32_t> range;
auto rangeProvided = context.params.isMember (jss::min_ledger) &&
context.params.isMember (jss::max_ledger);
if (rangeProvided)
if (args.ledgerRange)
{
try
{
auto const& min = context.params[jss::min_ledger].asUInt ();
auto const& max = context.params[jss::max_ledger].asUInt ();
constexpr uint16_t MAX_RANGE = 1000;
constexpr uint16_t MAX_RANGE = 1000;
if (args.ledgerRange->second < args.ledgerRange->first)
return {result, rpcINVALID_LGR_RANGE};
if (max < min)
return rpcError (rpcINVALID_LGR_RANGE);
if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
return {result, rpcEXCESSIVE_LGR_RANGE};
if (max - min > MAX_RANGE)
return rpcError (rpcEXCESSIVE_LGR_RANGE);
range = ClosedInterval<uint32_t> (min, max);
}
catch (...)
{
// One of the calls to `asUInt ()` failed.
return rpcError (rpcINVALID_LGR_RANGE);
}
range = ClosedInterval<uint32_t>(
args.ledgerRange->first, args.ledgerRange->second);
}
using pointer = Transaction::pointer;
auto ec {rpcSUCCESS};
pointer txn;
if (rangeProvided)
{
boost::variant<pointer, bool> v =
context.app.getMasterTransaction().fetch(
from_hex_text<uint256>(txid), range, ec);
if (v.which () == 1)
{
auto jvResult = Json::Value (Json::objectValue);
jvResult[jss::searched_all] = boost::get<bool> (v);
return rpcError (rpcTXN_NOT_FOUND, jvResult);
}
else
txn = boost::get<pointer> (v);
}
else
txn = context.app.getMasterTransaction().fetch(
from_hex_text<uint256>(txid), ec);
if (ec == rpcDB_DESERIALIZATION)
return rpcError (ec);
if (!txn)
return rpcError (rpcTXN_NOT_FOUND);
Json::Value ret = txn->getJson (JsonOptions::include_date, binary);
if (txn->getLedger () == 0)
return ret;
if (auto lgr = context.ledgerMaster.getLedgerBySeq (txn->getLedger ()))
{
bool okay = false;
if (binary)
{
std::string meta;
if (getMetaHex (*lgr, txn->getID (), meta))
{
ret[jss::meta] = meta;
okay = true;
}
}
else
{
auto rawMeta = lgr->txRead (txn->getID()).second;
if (rawMeta)
{
auto txMeta = std::make_shared<TxMeta>(
txn->getID(), lgr->seq(), *rawMeta);
okay = true;
auto meta = txMeta->getJson (JsonOptions::none);
insertDeliveredAmount (meta, context, txn, *txMeta);
ret[jss::meta] = std::move(meta);
}
}
if (okay)
ret[jss::validated] = isValidated (
context, lgr->info().seq, lgr->info().hash);
}
return ret;
}
std::pair<rpc::v1::GetTxResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<rpc::v1::GetTxRequest>& context)
{
// return values
rpc::v1::GetTxResponse result;
grpc::Status status = grpc::Status::OK;
// input
rpc::v1::GetTxRequest& request = context.params;
std::string const& hashBytes = request.hash();
uint256 hash = uint256::fromVoid(hashBytes.data());
// hash is included in the response
result.set_hash(request.hash());
std::shared_ptr<Transaction> txn;
auto ec{rpcSUCCESS};
// get the transaction
std::shared_ptr<Transaction> txn =
context.app.getMasterTransaction().fetch(hash, ec);
result.searchedAll = SearchedAll::unknown;
if (args.ledgerRange)
{
boost::variant<std::shared_ptr<Transaction>, bool> v =
context.app.getMasterTransaction().fetch(args.hash, range, ec);
if (v.which() == 1)
{
result.searchedAll =
boost::get<bool>(v) ? SearchedAll::yes : SearchedAll::no;
return {result, rpcTXN_NOT_FOUND};
}
else
{
txn = boost::get<std::shared_ptr<Transaction>>(v);
}
}
else
{
txn = context.app.getMasterTransaction().fetch(args.hash, ec);
}
if (ec == rpcDB_DESERIALIZATION)
{
auto errorInfo = RPC::get_error_info(ec);
grpc::Status errorStatus{grpc::StatusCode::INTERNAL,
errorInfo.message.c_str()};
return {result, errorStatus};
return {result, ec};
}
if (!txn)
{
grpc::Status errorStatus{grpc::StatusCode::NOT_FOUND, "txn not found"};
return {result, errorStatus};
}
std::shared_ptr<STTx const> stTxn = txn->getSTransaction();
if (stTxn->getTxnType() != ttPAYMENT)
{
auto getTypeStr = [&stTxn]() {
return TxFormats::getInstance()
.findByType(stTxn->getTxnType())
->getName();
};
grpc::Status errorStatus{grpc::StatusCode::UNIMPLEMENTED,
"txn type not supported: " + getTypeStr()};
return {result, errorStatus};
return {result, rpcTXN_NOT_FOUND};
}
// populate transaction data
if (request.binary())
result.txn = txn;
if (txn->getLedger() == 0)
{
Serializer s = stTxn->getSerializer();
result.set_transaction_binary(s.data(), s.size());
return {result, rpcSUCCESS};
}
else
{
RPC::populateTransaction(*result.mutable_transaction(), stTxn);
}
result.set_ledger_index(txn->getLedger());
std::shared_ptr<Ledger const> ledger =
context.ledgerMaster.getLedgerBySeq(txn->getLedger());
// get meta data
if (ledger)
{
if (request.binary())
bool ok = false;
if (args.binary)
{
SHAMapTreeNode::TNType type;
auto const item = ledger->txMap().peekItem(txn->getID(), type);
if (item && type == SHAMapTreeNode::tnTRANSACTION_MD)
{
ok = true;
SerialIter it(item->slice());
it.skip(it.getVLDataLength()); // skip transaction
Blob blob = it.getVL();
Slice slice = makeSlice(blob);
result.set_meta_binary(slice.data(), slice.size());
bool validated = isValidated(
context.ledgerMaster,
ledger->info().seq,
ledger->info().hash);
result.set_validated(validated);
result.meta = std::move(blob);
}
}
else
@@ -298,25 +190,239 @@ doTxGrpc(RPC::GRPCContext<rpc::v1::GetTxRequest>& context)
auto rawMeta = ledger->txRead(txn->getID()).second;
if (rawMeta)
{
auto txMeta = std::make_shared<TxMeta>(
ok = true;
result.meta = std::make_shared<TxMeta>(
txn->getID(), ledger->seq(), *rawMeta);
bool validated = isValidated(
context.ledgerMaster,
ledger->info().seq,
ledger->info().hash);
result.set_validated(validated);
RPC::populateMeta(*result.mutable_meta(), txMeta);
insertDeliveredAmount(
*result.mutable_meta()->mutable_delivered_amount(),
context,
txn,
*txMeta);
}
}
if (ok)
{
result.validated = isValidated(
context.ledgerMaster, ledger->info().seq, ledger->info().hash);
}
}
return {result, status};
return {result, rpcSUCCESS};
}
std::pair<org::xrpl::rpc::v1::GetTransactionResponse, grpc::Status>
populateProtoResponse(
std::pair<TxResult, RPC::Status> const& res,
TxArgs const& args,
RPC::GRPCContext<org::xrpl::rpc::v1::GetTransactionRequest> const& context)
{
org::xrpl::rpc::v1::GetTransactionResponse response;
grpc::Status status = grpc::Status::OK;
RPC::Status const& error = res.second;
TxResult const& result = res.first;
// handle errors
if (error.toErrorCode() != rpcSUCCESS)
{
if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
result.searchedAll != SearchedAll::unknown)
{
status = {
grpc::StatusCode::NOT_FOUND,
"txn not found. searched_all = " +
to_string(
(result.searchedAll == SearchedAll::yes ? "true"
: "false"))};
}
else
{
if (error.toErrorCode() == rpcTXN_NOT_FOUND)
status = {grpc::StatusCode::NOT_FOUND, "txn not found"};
else
status = {grpc::StatusCode::INTERNAL, error.message()};
}
}
// no errors
else if (result.txn)
{
auto& txn = result.txn;
std::shared_ptr<STTx const> stTxn = txn->getSTransaction();
if (args.binary)
{
Serializer s = stTxn->getSerializer();
response.set_transaction_binary(s.data(), s.size());
}
else
{
RPC::convert(*response.mutable_transaction(), stTxn);
}
response.set_hash(context.params.hash());
auto ledgerIndex = txn->getLedger();
response.set_ledger_index(ledgerIndex);
if (ledgerIndex)
{
auto ct =
context.app.getLedgerMaster().getCloseTimeBySeq(ledgerIndex);
if (ct)
response.mutable_date()->set_value(
ct->time_since_epoch().count());
}
RPC::convert(
*response.mutable_meta()->mutable_transaction_result(),
txn->getResult());
response.mutable_meta()->mutable_transaction_result()->set_result(
transToken(txn->getResult()));
// populate binary metadata
if (auto blob = std::get_if<Blob>(&result.meta))
{
assert(args.binary);
Slice slice = makeSlice(*blob);
response.set_meta_binary(slice.data(), slice.size());
}
// populate meta data
else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
{
auto& meta = *m;
if (meta)
{
RPC::convert(*response.mutable_meta(), meta);
auto amt =
getDeliveredAmount(context, stTxn, *meta, txn->getLedger());
if (amt)
{
RPC::convert(
*response.mutable_meta()->mutable_delivered_amount(),
*amt);
}
}
}
response.set_validated(result.validated);
}
return {response, status};
}
Json::Value
populateJsonResponse(
std::pair<TxResult, RPC::Status> const& res,
TxArgs const& args,
RPC::JsonContext const& context)
{
Json::Value response;
RPC::Status const& error = res.second;
TxResult const& result = res.first;
// handle errors
if (error.toErrorCode() != rpcSUCCESS)
{
if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
result.searchedAll != SearchedAll::unknown)
{
response = Json::Value(Json::objectValue);
response[jss::searched_all] =
(result.searchedAll == SearchedAll::yes);
error.inject(response);
}
else
{
error.inject(response);
}
}
// no errors
else if (result.txn)
{
response = result.txn->getJson(JsonOptions::include_date, args.binary);
// populate binary metadata
if (auto blob = std::get_if<Blob>(&result.meta))
{
assert(args.binary);
response[jss::meta] = strHex(makeSlice(*blob));
}
// populate meta data
else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
{
auto& meta = *m;
if (meta)
{
response[jss::meta] = meta->getJson(JsonOptions::none);
insertDeliveredAmount(
response[jss::meta], context, result.txn, *meta);
}
}
response[jss::validated] = result.validated;
}
return response;
}
Json::Value
doTxJson(RPC::JsonContext& context)
{
// Deserialize and validate JSON arguments
if (!context.params.isMember(jss::transaction))
return rpcError(rpcINVALID_PARAMS);
std::string txHash = context.params[jss::transaction].asString();
if (!isHexTxID(txHash))
return rpcError(rpcNOT_IMPL);
TxArgs args;
args.hash = from_hex_text<uint256>(txHash);
args.binary = context.params.isMember(jss::binary) &&
context.params[jss::binary].asBool();
if (context.params.isMember(jss::min_ledger) &&
context.params.isMember(jss::max_ledger))
{
try
{
args.ledgerRange = std::make_pair(
context.params[jss::min_ledger].asUInt(),
context.params[jss::max_ledger].asUInt());
}
catch (...)
{
// One of the calls to `asUInt ()` failed.
return rpcError(rpcINVALID_LGR_RANGE);
}
}
std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
return populateJsonResponse(res, args, context);
}
std::pair<org::xrpl::rpc::v1::GetTransactionResponse, grpc::Status>
doTxGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetTransactionRequest>& context)
{
// return values
org::xrpl::rpc::v1::GetTransactionResponse response;
grpc::Status status = grpc::Status::OK;
// input
org::xrpl::rpc::v1::GetTransactionRequest& request = context.params;
TxArgs args;
std::string const& hashBytes = request.hash();
args.hash = uint256::fromVoid(hashBytes.data());
if (args.hash.size() != hashBytes.size())
{
grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
"ledger hash malformed"};
return {response, errorStatus};
}
args.binary = request.binary();
if (request.ledger_range().ledger_index_min() != 0 &&
request.ledger_range().ledger_index_max() != 0)
{
args.ledgerRange = std::make_pair(
request.ledger_range().ledger_index_min(),
request.ledger_range().ledger_index_max());
}
std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
return populateProtoResponse(res, args, context);
}
} // namespace ripple

View File

@@ -39,43 +39,24 @@ namespace RPC {
would be calculated even when not needed, and in some circumstances they are
not trivial to compute.
GetFix1623Enabled is a callable that returns a bool
GetLedgerIndex is a callable that returns a LedgerIndex
GetCloseTime is a callable that returns a
boost::optional<NetClock::time_point>
*/
template<class GetFix1623Enabled, class GetLedgerIndex, class GetCloseTime>
void
insertDeliveredAmount(
Json::Value& meta,
GetFix1623Enabled const& getFix1623Enabled,
template <class GetLedgerIndex, class GetCloseTime>
std::optional<STAmount>
getDeliveredAmount(
GetLedgerIndex const& getLedgerIndex,
GetCloseTime const& getCloseTime,
std::shared_ptr<STTx const> serializedTx,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
{
TxType const tt{serializedTx->getTxnType()};
if (tt != ttPAYMENT &&
tt != ttCHECK_CASH &&
tt != ttACCOUNT_DELETE)
return;
if (tt == ttCHECK_CASH &&
!getFix1623Enabled())
return;
}
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return;
if (!serializedTx)
return {};
if (transactionMeta.hasDeliveredAmount())
{
meta[jss::delivered_amount] =
transactionMeta.getDeliveredAmount()
.getJson(JsonOptions::include_date);
return;
return transactionMeta.getDeliveredAmount();
}
if (serializedTx->isFieldPresent(sfAmount))
@@ -93,156 +74,161 @@ insertDeliveredAmount(
if (getLedgerIndex() >= 4594095 ||
getCloseTime() > NetClock::time_point{446000000s})
{
meta[jss::delivered_amount] =
serializedTx->getFieldAmount(sfAmount)
.getJson(JsonOptions::include_date);
return;
return serializedTx->getFieldAmount(sfAmount);
}
}
// report "unavailable" which cannot be parsed into a sensible amount.
meta[jss::delivered_amount] = Json::Value("unavailable");
return {};
}
// Returns true if transaction meta could contain a delivered amount field,
// based on transaction type, transaction result and whether fix1623 is enabled
template <class GetFix1623Enabled>
bool
canHaveDeliveredAmountHelp(
GetFix1623Enabled const& getFix1623Enabled,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
if (!serializedTx)
return false;
{
TxType const tt{serializedTx->getTxnType()};
if (tt != ttPAYMENT && tt != ttCHECK_CASH && tt != ttACCOUNT_DELETE)
return false;
if (tt == ttCHECK_CASH && !getFix1623Enabled())
return false;
}
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return false;
return true;
}
// Returns true if transaction meta could contain a delivered amount field,
// based on transaction type, transaction result and whether fix1623 is enabled
bool
canHaveDeliveredAmount(
RPC::Context const& context,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
// These lambdas are used to compute the values lazily
auto const getFix1623Enabled = [&context]() -> bool {
auto const view = context.app.openLedger().current();
if (!view)
return false;
return view->rules().enabled(fix1623);
};
return canHaveDeliveredAmountHelp(
getFix1623Enabled, serializedTx, transactionMeta);
}
void
insertDeliveredAmount(
Json::Value& meta,
ReadView const& ledger,
std::shared_ptr<STTx const> serializedTx,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta)
{
if (!serializedTx)
return;
auto const info = ledger.info();
auto const getFix1623Enabled = [&ledger] {
return ledger.rules().enabled(fix1623);
};
auto const getLedgerIndex = [&info] {
return info.seq;
};
auto const getCloseTime = [&info] {
return info.closeTime;
};
insertDeliveredAmount(
meta,
getFix1623Enabled,
getLedgerIndex,
getCloseTime,
std::move(serializedTx),
transactionMeta);
if (canHaveDeliveredAmountHelp(
getFix1623Enabled, serializedTx, transactionMeta))
{
auto const getLedgerIndex = [&info] { return info.seq; };
auto const getCloseTime = [&info] { return info.closeTime; };
auto amt = getDeliveredAmount(
getLedgerIndex,
getCloseTime,
std::move(serializedTx),
transactionMeta);
if (amt)
{
meta[jss::delivered_amount] =
amt->getJson(JsonOptions::include_date);
}
else
{
// report "unavailable" which cannot be parsed into a sensible
// amount.
meta[jss::delivered_amount] = Json::Value("unavailable");
}
}
}
template <class GetLedgerIndex>
std::optional<STAmount>
getDeliveredAmount(
RPC::Context const& context,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta,
GetLedgerIndex const& getLedgerIndex)
{
if (canHaveDeliveredAmount(context, serializedTx, transactionMeta))
{
auto const getCloseTime =
[&context,
&getLedgerIndex]() -> boost::optional<NetClock::time_point> {
return context.ledgerMaster.getCloseTimeBySeq(getLedgerIndex());
};
return getDeliveredAmount(
getLedgerIndex,
getCloseTime,
std::move(serializedTx),
transactionMeta);
}
return {};
}
std::optional<STAmount>
getDeliveredAmount(
RPC::Context const& context,
std::shared_ptr<STTx const> const& serializedTx,
TxMeta const& transactionMeta,
LedgerIndex const& ledgerIndex)
{
return getDeliveredAmount(
context, serializedTx, transactionMeta, [&ledgerIndex]() {
return ledgerIndex;
});
}
void
insertDeliveredAmount(
Json::Value& meta,
RPC::JsonContext& context,
std::shared_ptr<Transaction> transaction,
RPC::JsonContext const& context,
std::shared_ptr<Transaction> const& transaction,
TxMeta const& transactionMeta)
{
if (!transaction)
return;
auto const serializedTx = transaction->getSTransaction ();
if (! serializedTx)
return;
// These lambdas are used to compute the values lazily
auto const getFix1623Enabled = [&context]() -> bool {
auto const view = context.app.openLedger().current();
if (!view)
return false;
return view->rules().enabled(fix1623);
};
auto const getLedgerIndex = [&transaction]() -> LedgerIndex {
return transaction->getLedger();
};
auto const getCloseTime =
[&context, &transaction]() -> boost::optional<NetClock::time_point> {
return context.ledgerMaster.getCloseTimeBySeq(transaction->getLedger());
};
insertDeliveredAmount(
meta,
getFix1623Enabled,
getLedgerIndex,
getCloseTime,
std::move(serializedTx),
transactionMeta);
}
// TODO get rid of the code duplication between this function and the preceding
// function
void
insertDeliveredAmount(
rpc::v1::CurrencyAmount& proto,
RPC::Context& context,
std::shared_ptr<Transaction> transaction,
TxMeta const& transactionMeta)
{
if (!transaction)
return;
auto const serializedTx = transaction->getSTransaction();
if (!serializedTx)
return;
// These lambdas are used to compute the values lazily
auto const getFix1623Enabled = [&context]() -> bool {
auto const view = context.app.openLedger().current();
if (!view)
return false;
return view->rules().enabled(fix1623);
};
auto const getLedgerIndex = [&transaction]() -> LedgerIndex {
return transaction->getLedger();
};
auto const getCloseTime =
[&context, &transaction]() -> boost::optional<NetClock::time_point> {
return context.ledgerMaster.getCloseTimeBySeq(transaction->getLedger());
};
if (canHaveDeliveredAmount(context, serializedTx, transactionMeta))
{
TxType const tt{serializedTx->getTxnType()};
if (tt != ttPAYMENT &&
tt != ttCHECK_CASH &&
tt != ttACCOUNT_DELETE)
return;
auto amt = getDeliveredAmount(
context, serializedTx, transactionMeta, [&transaction]() {
return transaction->getLedger();
});
if (tt == ttCHECK_CASH &&
!getFix1623Enabled())
return;
}
// if the transaction failed nothing could have been delivered.
if (transactionMeta.getResultTER() != tesSUCCESS)
return;
if (transactionMeta.hasDeliveredAmount())
{
populateAmount(proto, transactionMeta.getDeliveredAmount());
return;
}
if (serializedTx->isFieldPresent(sfAmount))
{
using namespace std::chrono_literals;
// Ledger 4594095 is the first ledger in which the DeliveredAmount field
// was present when a partial payment was made and its absence indicates
// that the amount delivered is listed in the Amount field.
//
// If the ledger closed long after the DeliveredAmount code was deployed
// then its absence indicates that the amount delivered is listed in the
// Amount field. DeliveredAmount went live January 24, 2014.
// 446000000 is in Feb 2014, well after DeliveredAmount went live
if (getLedgerIndex() >= 4594095 ||
getCloseTime() > NetClock::time_point{446000000s})
if (amt)
{
populateAmount(proto, serializedTx->getFieldAmount(sfAmount));
return;
meta[jss::delivered_amount] =
amt->getJson(JsonOptions::include_date);
}
else
{
// report "unavailable" which cannot be parsed into a sensible
// amount.
meta[jss::delivered_amount] = Json::Value("unavailable");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 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_RPC_GRPCHELPERS_H_INCLUDED
#define RIPPLE_RPC_GRPCHELPERS_H_INCLUDED
#include "org/xrpl/rpc/v1/get_account_info.pb.h"
#include "org/xrpl/rpc/v1/ledger_objects.pb.h"
#include "org/xrpl/rpc/v1/meta.pb.h"
#include "org/xrpl/rpc/v1/transaction.pb.h"
#include <ripple/app/misc/TxQ.h>
#include <ripple/ledger/TxMeta.h>
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/STTx.h>
#include <functional>
namespace ripple {
namespace RPC {
void
convert(org::xrpl::rpc::v1::Meta& to, std::shared_ptr<TxMeta> const& from);
void
convert(
org::xrpl::rpc::v1::QueueData& to,
std::map<TxSeq, TxQ::AccountTxDetails const> const& from);
void
convert(
org::xrpl::rpc::v1::Transaction& to,
std::shared_ptr<STTx const> const& from);
void
convert(org::xrpl::rpc::v1::TransactionResult& to, TER from);
void
convert(org::xrpl::rpc::v1::AccountRoot& to, STObject const& from);
void
convert(org::xrpl::rpc::v1::SignerList& to, STObject const& from);
template <class T>
void
convert(T& to, STAmount const& from)
{
if (from.native())
{
to.mutable_value()->mutable_xrp_amount()->set_drops(from.xrp().drops());
}
else
{
Issue const& issue = from.issue();
org::xrpl::rpc::v1::IssuedCurrencyAmount* issued =
to.mutable_value()->mutable_issued_currency_amount();
issued->mutable_currency()->set_name(to_string(issue.currency));
issued->mutable_currency()->set_code(
issue.currency.data(), Currency::size());
issued->mutable_issuer()->set_address(toBase58(issue.account));
issued->set_value(to_string(from.iou()));
}
}
} // namespace RPC
} // namespace ripple
#endif

View File

@@ -65,7 +65,7 @@ Handler const handlerArray[] {
{ "account_channels", byRef (&doAccountChannels), Role::USER, NO_CONDITION },
{ "account_objects", byRef (&doAccountObjects), Role::USER, NO_CONDITION },
{ "account_offers", byRef (&doAccountOffers), Role::USER, NO_CONDITION },
{ "account_tx", byRef (&doAccountTxSwitch), Role::USER, NO_CONDITION },
{ "account_tx", byRef (&doAccountTxJson), Role::USER, NO_CONDITION },
{ "blacklist", byRef (&doBlackList), Role::ADMIN, NO_CONDITION },
{ "book_offers", byRef (&doBookOffers), Role::USER, NO_CONDITION },
{ "can_delete", byRef (&doCanDelete), Role::ADMIN, NO_CONDITION },
@@ -112,7 +112,7 @@ Handler const handlerArray[] {
{ "crawl_shards", byRef (&doCrawlShards), Role::ADMIN, NO_CONDITION },
{ "stop", byRef (&doStop), Role::ADMIN, NO_CONDITION },
{ "transaction_entry", byRef (&doTransactionEntry), Role::USER, NO_CONDITION },
{ "tx", byRef (&doTx), Role::USER, NEEDS_NETWORK_CONNECTION },
{ "tx", byRef (&doTxJson), Role::USER, NEEDS_NETWORK_CONNECTION },
{ "tx_history", byRef (&doTxHistory), Role::USER, NO_CONDITION },
{ "unl_list", byRef (&doUnlList), Role::ADMIN, NO_CONDITION },
{ "validation_create", byRef (&doValidationCreate), Role::ADMIN, NO_CONDITION },

View File

@@ -29,6 +29,8 @@
#include <ripple/rpc/impl/RPCHelpers.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <ripple/rpc/impl/GRPCHelpers.h>
namespace ripple {
namespace RPC {
@@ -199,12 +201,10 @@ template <class T>
Status
ledgerFromRequest(T& ledger, JsonContext& context)
{
static auto const minSequenceGap = 10;
ledger.reset();
auto& params = context.params;
auto& ledgerMaster = context.ledgerMaster;
auto indexValue = params[jss::ledger_index];
auto hashValue = params[jss::ledger_hash];
@@ -225,74 +225,32 @@ ledgerFromRequest(T& ledger, JsonContext& context)
return {rpcINVALID_PARAMS, "ledgerHashNotString"};
uint256 ledgerHash;
if (! ledgerHash.SetHex (hashValue.asString ()))
if(!ledgerHash.SetHex (hashValue.asString ()))
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
ledger = ledgerMaster.getLedgerByHash (ledgerHash);
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
return getLedger(ledger, ledgerHash, context);
}
else if (indexValue.isNumeric())
{
ledger = ledgerMaster.getLedgerBySeq (indexValue.asInt ());
if (ledger == nullptr)
{
auto cur = ledgerMaster.getCurrentLedger();
if (cur->info().seq == indexValue.asInt())
ledger = cur;
}
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
if (ledger->info().seq > ledgerMaster.getValidLedgerIndex() &&
isValidatedOld(ledgerMaster, context.app.config().standalone()))
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
return getLedger(ledger, indexValue.asInt(),context);
}
else
{
if (isValidatedOld (ledgerMaster, context.app.config().standalone()))
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
auto const index = indexValue.asString ();
if (index == "validated")
{
ledger = ledgerMaster.getValidatedLedger ();
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
assert (! ledger->open());
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
}
else
{
if (index.empty () || index == "current")
{
ledger = ledgerMaster.getCurrentLedger ();
assert (ledger->open());
}
return getLedger(ledger, LedgerShortcut::CURRENT, context);
else if (index == "closed")
{
ledger = ledgerMaster.getClosedLedger ();
assert (! ledger->open());
}
return getLedger(ledger, LedgerShortcut::CLOSED, context);
else
{
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
}
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
if (ledger->info().seq + minSequenceGap <
ledgerMaster.getValidLedgerIndex ())
{
ledger.reset ();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
}
}
@@ -304,88 +262,49 @@ template <class T>
Status
ledgerFromRequest(
T& ledger,
GRPCContext<rpc::v1::GetAccountInfoRequest>& context)
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context)
{
static auto const minSequenceGap = 10;
ledger.reset();
rpc::v1::GetAccountInfoRequest& request = context.params;
auto& ledgerMaster = context.ledgerMaster;
org::xrpl::rpc::v1::GetAccountInfoRequest& request = context.params;
using LedgerCase = rpc::v1::LedgerSpecifier::LedgerCase;
using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase;
LedgerCase ledgerCase = request.ledger().ledger_case();
if (ledgerCase == LedgerCase::kHash)
switch (ledgerCase)
{
uint256 ledgerHash = uint256::fromVoid(request.ledger().hash().data());
if (ledgerHash.size() != request.ledger().hash().size())
return {rpcINVALID_PARAMS, "ledgerHashMalformed"};
ledger = ledgerMaster.getLedgerByHash(ledgerHash);
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
}
else if (ledgerCase == LedgerCase::kSequence)
{
ledger = ledgerMaster.getLedgerBySeq(request.ledger().sequence());
if (ledger == nullptr)
case LedgerCase::kHash:
{
auto cur = ledgerMaster.getCurrentLedger();
if (cur->info().seq == request.ledger().sequence())
ledger = cur;
uint256 ledgerHash =
uint256::fromVoid(request.ledger().hash().data());
return getLedger(ledger, ledgerHash, context);
}
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
if (ledger->info().seq > ledgerMaster.getValidLedgerIndex() &&
isValidatedOld(ledgerMaster, context.app.config().standalone()))
case LedgerCase::kSequence:
return getLedger(ledger, request.ledger().sequence(), context);
case LedgerCase::kShortcut:
[[fallthrough]];
case LedgerCase::LEDGER_NOT_SET:
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
}
else if (
ledgerCase == LedgerCase::kShortcut ||
ledgerCase == LedgerCase::LEDGER_NOT_SET)
{
if (isValidatedOld(ledgerMaster, context.app.config().standalone()))
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
auto const shortcut = request.ledger().shortcut();
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
{
ledger = ledgerMaster.getValidatedLedger();
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
assert(!ledger->open());
}
else
{
// note, if unspecified, defaults to current ledger
if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED ||
shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT)
auto const shortcut = request.ledger().shortcut();
if (shortcut ==
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED)
return getLedger(ledger, LedgerShortcut::VALIDATED, context);
else
{
ledger = ledgerMaster.getCurrentLedger();
assert(ledger->open());
}
else if (shortcut == rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
{
ledger = ledgerMaster.getClosedLedger();
assert(!ledger->open());
}
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
if (ledger->info().seq + minSequenceGap <
ledgerMaster.getValidLedgerIndex())
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
// note, if unspecified, defaults to current ledger
if (shortcut ==
org::xrpl::rpc::v1::LedgerSpecifier::
SHORTCUT_UNSPECIFIED ||
shortcut ==
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT)
{
return getLedger(ledger, LedgerShortcut::CURRENT, context);
}
else if (
shortcut ==
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED)
{
return getLedger(ledger, LedgerShortcut::CLOSED, context);
}
}
}
}
@@ -397,7 +316,103 @@ ledgerFromRequest(
template Status
ledgerFromRequest<>(
std::shared_ptr<ReadView const>&,
GRPCContext<rpc::v1::GetAccountInfoRequest>&);
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>&);
Status
getLedger(
std::shared_ptr<ReadView const>& ledger,
uint256 const& ledgerHash,
Context& context)
{
ledger = context.ledgerMaster.getLedgerByHash(ledgerHash);
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
return Status::OK;
}
template <class T>
Status
getLedger(T& ledger, uint32_t ledgerIndex, Context& context)
{
ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex);
if (ledger == nullptr)
{
auto cur = context.ledgerMaster.getCurrentLedger();
if (cur->info().seq == ledgerIndex)
{
ledger = cur;
}
}
if (ledger == nullptr)
return {rpcLGR_NOT_FOUND, "ledgerNotFound"};
if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() &&
isValidatedOld(context.ledgerMaster, context.app.config().standalone()))
{
ledger.reset();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
return Status::OK;
}
template <class T>
Status
getLedger(T& ledger, LedgerShortcut shortcut, Context& context)
{
if (isValidatedOld (context.ledgerMaster, context.app.config().standalone()))
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
if (shortcut == LedgerShortcut::VALIDATED)
{
ledger = context.ledgerMaster.getValidatedLedger ();
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
assert (! ledger->open());
}
else
{
if (shortcut == LedgerShortcut::CURRENT)
{
ledger = context.ledgerMaster.getCurrentLedger ();
assert (ledger->open());
}
else if (shortcut == LedgerShortcut::CLOSED)
{
ledger = context.ledgerMaster.getClosedLedger ();
assert (! ledger->open());
}
else
{
return {rpcINVALID_PARAMS, "ledgerIndexMalformed"};
}
if (ledger == nullptr)
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
static auto const minSequenceGap = 10;
if (ledger->info().seq + minSequenceGap <
context.ledgerMaster.getValidLedgerIndex ())
{
ledger.reset ();
return {rpcNO_NETWORK, "InsufficientNetworkMode"};
}
}
return Status::OK;
}
//Explicit instantiaion of above three functions
template Status
getLedger<>(std::shared_ptr<ReadView const>&,
uint32_t, Context&);
template Status
getLedger<>(std::shared_ptr<ReadView const>&,
LedgerShortcut shortcut, Context&);
bool
isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
@@ -823,672 +838,6 @@ chooseLedgerEntryType(Json::Value const& params)
return result;
}
void
populateAccountRoot(rpc::v1::AccountRoot& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfAccount))
{
AccountID account = obj.getAccountID(sfAccount);
proto.mutable_account()->set_address(toBase58(account));
}
if (obj.isFieldPresent(sfBalance))
{
STAmount amount = obj.getFieldAmount(sfBalance);
proto.mutable_balance()->set_drops(amount.xrp().drops());
}
if (obj.isFieldPresent(sfSequence))
{
proto.set_sequence(obj.getFieldU32(sfSequence));
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfOwnerCount))
{
proto.set_owner_count(obj.getFieldU32(sfOwnerCount));
}
if (obj.isFieldPresent(sfPreviousTxnID))
{
auto field = obj.getFieldH256(sfPreviousTxnID);
proto.set_previous_transaction_id(field.data(), field.size());
}
if (obj.isFieldPresent(sfPreviousTxnLgrSeq))
{
proto.set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
}
if (obj.isFieldPresent(sfAccountTxnID))
{
auto field = obj.getFieldH256(sfAccountTxnID);
proto.set_account_transaction_id(field.data(), field.size());
}
if (obj.isFieldPresent(sfDomain))
{
auto field = obj.getFieldH256(sfDomain);
proto.set_domain(field.data(), field.size());
}
if (obj.isFieldPresent(sfEmailHash))
{
auto field = obj.getFieldH128(sfEmailHash);
proto.set_email_hash(field.data(), field.size());
}
if (obj.isFieldPresent(sfMessageKey))
{
auto field = obj.getFieldVL(sfMessageKey);
proto.set_message_key(field.data(), field.size());
}
if (obj.isFieldPresent(sfRegularKey))
{
proto.set_regular_key(toBase58(obj.getAccountID(sfRegularKey)));
}
if (obj.isFieldPresent(sfTickSize))
{
proto.set_tick_size(obj.getFieldU8(sfTickSize));
}
if (obj.isFieldPresent(sfTransferRate))
{
proto.set_transfer_rate(obj.getFieldU32(sfTransferRate));
}
}
void
populateRippleState(rpc::v1::RippleState& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfBalance))
{
STAmount amount = obj.getFieldAmount(sfBalance);
populateAmount(*proto.mutable_balance(), amount);
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfLowLimit))
{
STAmount amount = obj.getFieldAmount(sfLowLimit);
populateAmount(*proto.mutable_low_limit(), amount);
}
if (obj.isFieldPresent(sfHighLimit))
{
STAmount amount = obj.getFieldAmount(sfHighLimit);
populateAmount(*proto.mutable_high_limit(), amount);
}
if (obj.isFieldPresent(sfLowNode))
{
proto.set_low_node(obj.getFieldU64(sfLowNode));
}
if (obj.isFieldPresent(sfHighNode))
{
proto.set_high_node(obj.getFieldU64(sfHighNode));
}
if (obj.isFieldPresent(sfLowQualityIn))
{
proto.set_low_quality_in(obj.getFieldU32(sfLowQualityIn));
}
if (obj.isFieldPresent(sfLowQualityOut))
{
proto.set_low_quality_out(obj.getFieldU32(sfLowQualityOut));
}
if (obj.isFieldPresent(sfHighQualityIn))
{
proto.set_high_quality_in(obj.getFieldU32(sfHighQualityIn));
}
if (obj.isFieldPresent(sfHighQualityOut))
{
proto.set_high_quality_out(obj.getFieldU32(sfHighQualityOut));
}
}
void
populateOffer(rpc::v1::Offer& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfAccount))
{
AccountID account = obj.getAccountID(sfAccount);
proto.set_account(toBase58(account));
}
if (obj.isFieldPresent(sfSequence))
{
proto.set_sequence(obj.getFieldU32(sfSequence));
}
if (obj.isFieldPresent(sfFlags))
{
proto.set_flags(obj.getFieldU32(sfFlags));
}
if (obj.isFieldPresent(sfTakerPays))
{
STAmount amount = obj.getFieldAmount(sfTakerPays);
populateAmount(*proto.mutable_taker_pays(), amount);
}
if (obj.isFieldPresent(sfTakerGets))
{
STAmount amount = obj.getFieldAmount(sfTakerGets);
populateAmount(*proto.mutable_taker_gets(), amount);
}
if (obj.isFieldPresent(sfBookDirectory))
{
auto field = obj.getFieldVL(sfBookDirectory);
proto.set_book_directory(field.data(), field.size());
}
if (obj.isFieldPresent(sfBookNode))
{
proto.set_book_node(obj.getFieldU64(sfBookNode));
}
if (obj.isFieldPresent(sfExpiration))
{
proto.set_expiration(obj.getFieldU32(sfExpiration));
}
}
void
populateSignerList(rpc::v1::SignerList& proto, STObject const& obj)
{
proto.set_flags(obj.getFieldU32(sfFlags));
auto prevTxnID = obj.getFieldH256(sfPreviousTxnID);
proto.set_previous_txn_id(prevTxnID.data(), prevTxnID.size());
proto.set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
proto.set_owner_node(obj.getFieldU64(sfOwnerNode));
proto.set_signer_list_id(obj.getFieldU32(sfSignerListID));
proto.set_signer_quorum(obj.getFieldU32(sfSignerQuorum));
STArray const& signerEntries = obj.getFieldArray(sfSignerEntries);
for (auto it = signerEntries.begin(); it != signerEntries.end(); ++it)
{
rpc::v1::SignerEntry& signerEntryProto = *proto.add_signer_entries();
signerEntryProto.mutable_account()->set_address(
toBase58(it->getAccountID(sfAccount)));
signerEntryProto.set_signer_weight(it->getFieldU16(sfSignerWeight));
}
}
void
populateQueueData(
rpc::v1::QueueData& proto,
std::map<TxSeq, TxQ::AccountTxDetails const> const& txs)
{
if (!txs.empty())
{
proto.set_txn_count(txs.size());
proto.set_lowest_sequence(txs.begin()->first);
proto.set_highest_sequence(txs.rbegin()->first);
boost::optional<bool> anyAuthChanged(false);
boost::optional<XRPAmount> totalSpend(0);
for (auto const& [txSeq, txDetails] : txs)
{
rpc::v1::QueuedTransaction& qt = *proto.add_transactions();
qt.set_sequence(txSeq);
qt.set_fee_level(txDetails.feeLevel.fee());
if (txDetails.lastValid)
qt.set_last_ledger_sequence(*txDetails.lastValid);
if (txDetails.consequences)
{
qt.mutable_fee()->set_drops(
txDetails.consequences->fee.drops());
auto spend = txDetails.consequences->potentialSpend +
txDetails.consequences->fee;
qt.mutable_max_spend_drops()->set_drops(spend.drops());
if (totalSpend)
*totalSpend += spend;
auto authChanged =
txDetails.consequences->category == TxConsequences::blocker;
if (authChanged)
anyAuthChanged.emplace(authChanged);
qt.set_auth_change(authChanged);
}
else
{
if (anyAuthChanged && !*anyAuthChanged)
anyAuthChanged.reset();
totalSpend.reset();
}
}
if (anyAuthChanged)
proto.set_auth_change_queued(*anyAuthChanged);
if (totalSpend)
proto.mutable_max_spend_drops_total()->set_drops(
(*totalSpend).drops());
}
}
void
populateDirectoryNode(rpc::v1::DirectoryNode& proto, STObject const& obj)
{
if (obj.isFieldPresent(sfOwner))
{
AccountID ownerAccount = obj.getAccountID(sfAccount);
proto.set_owner(toBase58(ownerAccount));
}
if (obj.isFieldPresent(sfTakerPaysCurrency))
{
uint160 tpCurr = obj.getFieldH160(sfTakerPaysCurrency);
proto.mutable_taker_pays_currency()->set_code(
tpCurr.data(), tpCurr.size());
}
if (obj.isFieldPresent(sfTakerPaysIssuer))
{
uint160 tpIss = obj.getFieldH160(sfTakerPaysIssuer);
proto.set_taker_pays_issuer(tpIss.data(), tpIss.size());
}
if (obj.isFieldPresent(sfTakerGetsCurrency))
{
uint160 tgCurr = obj.getFieldH160(sfTakerGetsCurrency);
proto.mutable_taker_gets_currency()->set_code(
tgCurr.data(), tgCurr.size());
}
if (obj.isFieldPresent(sfTakerGetsIssuer))
{
uint160 tgIss = obj.getFieldH160(sfTakerGetsIssuer);
proto.set_taker_gets_issuer(tgIss.data(), tgIss.size());
}
if (obj.isFieldPresent(sfIndexes))
{
const STVector256& vec = obj.getFieldV256(sfIndexes);
for (size_t i = 0; i < vec.size(); ++i)
{
uint256 const& elt = vec[i];
proto.add_indexes(elt.data(), elt.size());
}
}
if (obj.isFieldPresent(sfRootIndex))
{
uint256 rootIndex = obj.getFieldH256(sfRootIndex);
proto.set_root_index(rootIndex.data(), rootIndex.size());
}
if (obj.isFieldPresent(sfIndexNext))
{
proto.set_index_next(obj.getFieldU64(sfIndexNext));
}
if (obj.isFieldPresent(sfIndexPrevious))
{
proto.set_index_previous(obj.getFieldU64(sfIndexPrevious));
}
}
void
populateLedgerEntryType(rpc::v1::AffectedNode& proto, std::uint16_t lgrType)
{
switch (lgrType)
{
case ltACCOUNT_ROOT:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_ACCOUNT_ROOT);
break;
case ltDIR_NODE:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_DIRECTORY_NODE);
break;
case ltRIPPLE_STATE:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_RIPPLE_STATE);
break;
case ltSIGNER_LIST:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_SIGNER_LIST);
break;
case ltOFFER:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_OFFER);
break;
case ltLEDGER_HASHES:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_LEDGER_HASHES);
break;
case ltAMENDMENTS:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_AMENDMENTS);
break;
case ltFEE_SETTINGS:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_FEE_SETTINGS);
break;
case ltESCROW:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_ESCROW);
break;
case ltPAYCHAN:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_PAY_CHANNEL);
break;
case ltCHECK:
proto.set_ledger_entry_type(rpc::v1::LEDGER_ENTRY_TYPE_CHECK);
break;
case ltDEPOSIT_PREAUTH:
proto.set_ledger_entry_type(
rpc::v1::LEDGER_ENTRY_TYPE_DEPOSIT_PREAUTH);
break;
}
}
template <class T>
void
populateFields(T& proto, STObject const& obj, std::uint16_t type)
{
if (type == ltACCOUNT_ROOT)
{
RPC::populateAccountRoot(*proto.mutable_account_root(), obj);
}
else if (type == ltRIPPLE_STATE)
{
RPC::populateRippleState(*proto.mutable_ripple_state(), obj);
}
else if (type == ltOFFER)
{
RPC::populateOffer(*proto.mutable_offer(), obj);
}
else if (type == ltDIR_NODE)
{
RPC::populateDirectoryNode(*proto.mutable_directory_node(), obj);
}
else
{
// Ledger object not supported by protobuf/grpc yet
}
}
void
populateMeta(rpc::v1::Meta& proto, std::shared_ptr<TxMeta> txMeta)
{
proto.set_transaction_index(txMeta->getIndex());
populateTransactionResultType(
*proto.mutable_transaction_result(), txMeta->getResultTER());
proto.mutable_transaction_result()->set_result(
transToken(txMeta->getResultTER()));
STArray& nodes = txMeta->getNodes();
for (auto it = nodes.begin(); it != nodes.end(); ++it)
{
STObject& obj = *it;
rpc::v1::AffectedNode* node = proto.add_affected_nodes();
// ledger index
uint256 ledgerIndex = obj.getFieldH256(sfLedgerIndex);
node->set_ledger_index(ledgerIndex.data(), ledgerIndex.size());
// ledger entry type
std::uint16_t lgrType = obj.getFieldU16(sfLedgerEntryType);
populateLedgerEntryType(*node, lgrType);
// modified node
if (obj.getFName() == sfModifiedNode)
{
// final fields
if (obj.isFieldPresent(sfFinalFields))
{
STObject& finalFields =
obj.getField(sfFinalFields).downcast<STObject>();
rpc::v1::LedgerObject* finalFieldsProto =
node->mutable_modified_node()->mutable_final_fields();
populateFields(*finalFieldsProto, finalFields, lgrType);
}
// previous fields
if (obj.isFieldPresent(sfPreviousFields))
{
STObject& prevFields =
obj.getField(sfPreviousFields).downcast<STObject>();
rpc::v1::LedgerObject* prevFieldsProto =
node->mutable_modified_node()->mutable_previous_fields();
populateFields(*prevFieldsProto, prevFields, lgrType);
}
// prev txn id and prev txn ledger seq
uint256 prevTxnId = obj.getFieldH256(sfPreviousTxnID);
node->mutable_modified_node()->set_previous_transaction_id(
prevTxnId.data(), prevTxnId.size());
node->mutable_modified_node()
->set_previous_transaction_ledger_sequence(
obj.getFieldU32(sfPreviousTxnLgrSeq));
}
// created node
else if (obj.getFName() == sfCreatedNode)
{
// new fields
if (obj.isFieldPresent(sfNewFields))
{
STObject& newFields =
obj.getField(sfNewFields).downcast<STObject>();
rpc::v1::LedgerObject* newFieldsProto =
node->mutable_created_node()->mutable_new_fields();
populateFields(*newFieldsProto, newFields, lgrType);
}
}
// deleted node
else if (obj.getFName() == sfDeletedNode)
{
// final fields
if (obj.isFieldPresent(sfFinalFields))
{
STObject& finalFields =
obj.getField(sfFinalFields).downcast<STObject>();
rpc::v1::LedgerObject* finalFieldsProto =
node->mutable_deleted_node()->mutable_final_fields();
populateFields(*finalFieldsProto, finalFields, lgrType);
}
}
}
}
void
populateAmount(rpc::v1::CurrencyAmount& proto, STAmount const& amount)
{
if (amount.native())
{
proto.mutable_xrp_amount()->set_drops(amount.xrp().drops());
}
else
{
rpc::v1::IssuedCurrencyAmount* issued =
proto.mutable_issued_currency_amount();
Issue const& issue = amount.issue();
Currency currency = issue.currency;
issued->mutable_currency()->set_name(to_string(issue.currency));
issued->mutable_currency()->set_code(currency.data(), currency.size());
issued->set_value(to_string(amount.iou()));
issued->mutable_issuer()->set_address(toBase58(issue.account));
}
}
void
populateTransaction(
rpc::v1::Transaction& proto,
std::shared_ptr<STTx const> txnSt)
{
AccountID account = txnSt->getAccountID(sfAccount);
proto.mutable_account()->set_address(toBase58(account));
STAmount amount = txnSt->getFieldAmount(sfAmount);
populateAmount(*proto.mutable_payment()->mutable_amount(), amount);
AccountID accountDest = txnSt->getAccountID(sfDestination);
proto.mutable_payment()->mutable_destination()->set_address(
toBase58(accountDest));
STAmount fee = txnSt->getFieldAmount(sfFee);
proto.mutable_fee()->set_drops(fee.xrp().drops());
proto.set_sequence(txnSt->getFieldU32(sfSequence));
Blob signingPubKey = txnSt->getFieldVL(sfSigningPubKey);
proto.set_signing_public_key(signingPubKey.data(), signingPubKey.size());
proto.set_flags(txnSt->getFieldU32(sfFlags));
proto.set_last_ledger_sequence(txnSt->getFieldU32(sfLastLedgerSequence));
Blob blob = txnSt->getFieldVL(sfTxnSignature);
proto.set_signature(blob.data(), blob.size());
if (txnSt->isFieldPresent(sfSourceTag))
{
proto.set_source_tag(txnSt->getFieldU32(sfSourceTag));
}
if (txnSt->isFieldPresent(sfAccountTxnID))
{
auto field = txnSt->getFieldH256(sfAccountTxnID);
proto.set_account_transaction_id(field.data(), field.size());
}
if (txnSt->isFieldPresent(sfMemos))
{
auto memos = txnSt->getFieldArray(sfMemos);
for (auto it = memos.begin(); it != memos.end(); ++it)
{
rpc::v1::Memo* elt = proto.add_memos();
auto memo = it->getField(sfMemo).downcast<STObject>();
if (memo.isFieldPresent(sfMemoData))
{
auto memoData = memo.getFieldVL(sfMemoData);
elt->set_memo_data(memoData.data(), memoData.size());
}
if (memo.isFieldPresent(sfMemoFormat))
{
auto memoFormat = memo.getFieldVL(sfMemoFormat);
elt->set_memo_format(memoFormat.data(), memoFormat.size());
}
if (memo.isFieldPresent(sfMemoType))
{
auto memoType = memo.getFieldVL(sfMemoType);
elt->set_memo_type(memoType.data(), memoType.size());
}
}
}
if (txnSt->isFieldPresent(sfSigners))
{
auto signers = txnSt->getFieldArray(sfSigners);
for (auto it = signers.begin(); it != signers.end(); ++it)
{
rpc::v1::Signer* elt = proto.add_signers();
auto signer = it->getField(sfSigner).downcast<STObject>();
if (signer.isFieldPresent(sfAccount))
{
elt->mutable_account()->set_address(
toBase58(signer.getAccountID(sfAccount)));
}
if (signer.isFieldPresent(sfTxnSignature))
{
auto sig = signer.getFieldVL(sfTxnSignature);
elt->set_transaction_signature(sig.data(), sig.size());
}
if (signer.isFieldPresent(sfSigningPubKey))
{
auto pubKey = signer.getFieldVL(sfSigningPubKey);
elt->set_signing_public_key(pubKey.data(), pubKey.size());
}
}
}
if (safe_cast<TxType>(txnSt->getFieldU16(sfTransactionType)) ==
TxType::ttPAYMENT)
{
if (txnSt->isFieldPresent(sfSendMax))
{
STAmount const& sendMax = txnSt->getFieldAmount(sfSendMax);
populateAmount(
*proto.mutable_payment()->mutable_send_max(), sendMax);
}
if (txnSt->isFieldPresent(sfInvoiceID))
{
auto invoice = txnSt->getFieldH256(sfInvoiceID);
proto.mutable_payment()->set_invoice_id(
invoice.data(), invoice.size());
}
if (txnSt->isFieldPresent(sfDestinationTag))
{
proto.mutable_payment()->set_destination_tag(
txnSt->getFieldU32(sfDestinationTag));
}
// populate path data
STPathSet const& pathset = txnSt->getFieldPathSet(sfPaths);
for (auto it = pathset.begin(); it < pathset.end(); ++it)
{
STPath const& path = *it;
rpc::v1::Path* protoPath = proto.mutable_payment()->add_paths();
for (auto it2 = path.begin(); it2 != path.end(); ++it2)
{
rpc::v1::PathElement* protoElement = protoPath->add_elements();
STPathElement const& elt = *it2;
if (elt.isOffer())
{
if (elt.hasCurrency())
{
Currency const& currency = elt.getCurrency();
protoElement->mutable_currency()->set_name(
to_string(currency));
}
if (elt.hasIssuer())
{
AccountID const& issuer = elt.getIssuerID();
protoElement->mutable_issuer()->set_address(
toBase58(issuer));
}
}
else
{
AccountID const& pathAccount = elt.getAccountID();
protoElement->mutable_account()->set_address(
toBase58(pathAccount));
}
}
}
}
}
void
populateTransactionResultType(rpc::v1::TransactionResult& proto, TER result)
{
if (isTecClaim(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEC);
}
if (isTefFailure(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEF);
}
if (isTelLocal(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEL);
}
if (isTemMalformed(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TEM);
}
if (isTerRetry(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TER);
}
if (isTesSuccess(result))
{
proto.set_result_type(rpc::v1::TransactionResult::RESULT_TYPE_TES);
}
}
beast::SemanticVersion const firstVersion("1.0.0");
beast::SemanticVersion const goodVersion("1.0.0");
beast::SemanticVersion const lastVersion("1.0.0");

View File

@@ -29,9 +29,9 @@
#include <ripple/rpc/Status.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/TxQ.h>
#include <rpc/v1/xrp_ledger.pb.h>
#include <org/xrpl/rpc/v1/xrp_ledger.pb.h>
#include <boost/optional.hpp>
#include <rpc/v1/xrp_ledger.pb.h>
#include <org/xrpl/rpc/v1/xrp_ledger.pb.h>
namespace Json {
class Value;
@@ -82,6 +82,37 @@ getAccountObjects (ReadView const& ledger, AccountID const& account,
boost::optional<std::vector<LedgerEntryType>> const& typeFilter, uint256 dirIndex,
uint256 const& entryIndex, std::uint32_t const limit, Json::Value& jvResult);
/** Get ledger by hash
If there is no error in the return value, the ledger pointer will have
been filled
*/
Status
getLedger(std::shared_ptr<ReadView const>& ledger, uint256 const & ledgerHash, Context& context);
/** Get ledger by sequence
If there is no error in the return value, the ledger pointer will have
been filled
*/
template <class T>
Status
getLedger(T& ledger, uint32_t ledgerIndex, Context& context);
enum LedgerShortcut
{
CURRENT,
CLOSED,
VALIDATED
};
/** Get ledger specified in shortcut.
If there is no error in the return value, the ledger pointer will have
been filled
*/
template <class T>
Status
getLedger(T& ledger, LedgerShortcut shortcut, Context& context);
/** Look up a ledger from a request and fill a Json::Result with either
an error, or data representing a ledger.
@@ -103,7 +134,7 @@ template <class T>
Status
ledgerFromRequest(
T& ledger,
GRPCContext<rpc::v1::GetAccountInfoRequest>& context);
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context);
bool
isValidated(LedgerMaster& ledgerMaster, ReadView const& ledger,
@@ -204,45 +235,6 @@ std::pair<RPC::Status, LedgerEntryType>
*/
unsigned int getAPIVersionNumber(const Json::Value & value);
/*
* For all of the below populate* functions, the proto argument is an
* output parameter, and is populated with the data stored in the
* serialized object
*/
void
populateAccountRoot(rpc::v1::AccountRoot& proto, STObject const& obj);
void
populateRippleState(rpc::v1::RippleState& proto, STObject const& obj);
void
populateOffer(rpc::v1::Offer& proto, STObject const& obj);
void
populateSignerList(rpc::v1::SignerList& proto, STObject const& obj);
void
populateQueueData(
rpc::v1::QueueData& proto,
std::map<TxSeq, TxQ::AccountTxDetails const> const& txs);
void
populateDirectoryNode(rpc::v1::DirectoryNode& proto, STObject const& obj);
void
populateMeta(rpc::v1::Meta& proto, std::shared_ptr<TxMeta> txMeta);
void
populateTransaction(
rpc::v1::Transaction& proto,
std::shared_ptr<STTx const> txnSt);
void
populateAmount(rpc::v1::CurrencyAmount& proto, STAmount const& amount);
void
populateTransactionResultType(rpc::v1::TransactionResult& proto, TER result);
} // RPC
} // ripple

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: