mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 22:15:52 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
14
src/ripple/proto/org/xrpl/rpc/v1/account.proto
Normal file
14
src/ripple/proto/org/xrpl/rpc/v1/account.proto
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
476
src/ripple/proto/org/xrpl/rpc/v1/common.proto
Normal file
476
src/ripple/proto/org/xrpl/rpc/v1/common.proto
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
57
src/ripple/proto/org/xrpl/rpc/v1/get_fee.proto
Normal file
57
src/ripple/proto/org/xrpl/rpc/v1/get_fee.proto
Normal 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];
|
||||
}
|
||||
56
src/ripple/proto/org/xrpl/rpc/v1/get_transaction.proto
Normal file
56
src/ripple/proto/org/xrpl/rpc/v1/get_transaction.proto
Normal 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;
|
||||
}
|
||||
37
src/ripple/proto/org/xrpl/rpc/v1/ledger.proto
Normal file
37
src/ripple/proto/org/xrpl/rpc/v1/ledger.proto
Normal 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;
|
||||
};
|
||||
|
||||
331
src/ripple/proto/org/xrpl/rpc/v1/ledger_objects.proto
Normal file
331
src/ripple/proto/org/xrpl/rpc/v1/ledger_objects.proto
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
319
src/ripple/proto/org/xrpl/rpc/v1/transaction.proto
Normal file
319
src/ripple/proto/org/xrpl/rpc/v1/transaction.proto
Normal 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;
|
||||
}
|
||||
30
src/ripple/proto/org/xrpl/rpc/v1/xrp_ledger.proto
Normal file
30
src/ripple/proto/org/xrpl/rpc/v1/xrp_ledger.proto
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1779
src/ripple/rpc/impl/GRPCHelpers.cpp
Normal file
1779
src/ripple/rpc/impl/GRPCHelpers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
87
src/ripple/rpc/impl/GRPCHelpers.h
Normal file
87
src/ripple/rpc/impl/GRPCHelpers.h
Normal 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
|
||||
@@ -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 },
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
42
src/test/jtx/account_txn_id.h
Normal file
42
src/test/jtx/account_txn_id.h
Normal 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
|
||||
35
src/test/jtx/impl/account_txn_id.cpp
Normal file
35
src/test/jtx/impl/account_txn_id.cpp
Normal 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
|
||||
36
src/test/jtx/impl/invoice_id.cpp
Normal file
36
src/test/jtx/impl/invoice_id.cpp
Normal 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
|
||||
37
src/test/jtx/impl/last_ledger_sequence.cpp
Normal file
37
src/test/jtx/impl/last_ledger_sequence.cpp
Normal 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
42
src/test/jtx/invoice_id.h
Normal 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
|
||||
44
src/test/jtx/last_ledger_sequence.h
Normal file
44
src/test/jtx/last_ledger_sequence.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user