mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 02:25:52 +00:00
Remove gRPC code previously used for the xpring SDK
This commit is contained in:
@@ -639,7 +639,6 @@ 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
|
||||
@@ -909,7 +908,6 @@ if (tests)
|
||||
src/test/protocol/BuildInfo_test.cpp
|
||||
src/test/protocol/InnerObjectFormats_test.cpp
|
||||
src/test/protocol/Issue_test.cpp
|
||||
src/test/protocol/KnownFormatToGRPC_test.cpp
|
||||
src/test/protocol/Hooks_test.cpp
|
||||
src/test/protocol/PublicKey_test.cpp
|
||||
src/test/protocol/Quality_test.cpp
|
||||
@@ -944,7 +942,6 @@ if (tests)
|
||||
src/test/rpc/DepositAuthorized_test.cpp
|
||||
src/test/rpc/DeliveredAmount_test.cpp
|
||||
src/test/rpc/Feature_test.cpp
|
||||
src/test/rpc/Fee_test.cpp
|
||||
src/test/rpc/GatewayBalances_test.cpp
|
||||
src/test/rpc/GetCounts_test.cpp
|
||||
src/test/rpc/JSONRPC_test.cpp
|
||||
@@ -967,12 +964,10 @@ if (tests)
|
||||
src/test/rpc/ServerInfo_test.cpp
|
||||
src/test/rpc/ShardArchiveHandler_test.cpp
|
||||
src/test/rpc/Status_test.cpp
|
||||
src/test/rpc/Submit_test.cpp
|
||||
src/test/rpc/Subscribe_test.cpp
|
||||
src/test/rpc/Transaction_test.cpp
|
||||
src/test/rpc/TransactionEntry_test.cpp
|
||||
src/test/rpc/TransactionHistory_test.cpp
|
||||
src/test/rpc/Tx_test.cpp
|
||||
src/test/rpc/ValidatorInfo_test.cpp
|
||||
src/test/rpc/ValidatorRPC_test.cpp
|
||||
src/test/rpc/Version_test.cpp
|
||||
|
||||
@@ -113,7 +113,6 @@ test.consensus > ripple.basics
|
||||
test.consensus > ripple.beast
|
||||
test.consensus > ripple.consensus
|
||||
test.consensus > ripple.ledger
|
||||
test.consensus > ripple.rpc
|
||||
test.consensus > test.csf
|
||||
test.consensus > test.toplevel
|
||||
test.consensus > test.unit_test
|
||||
|
||||
@@ -1629,10 +1629,10 @@ ip = 127.0.0.1
|
||||
admin = 127.0.0.1
|
||||
protocol = ws
|
||||
|
||||
#[port_grpc]
|
||||
#port = 50051
|
||||
#ip = 0.0.0.0
|
||||
#secure_gateway = 127.0.0.1
|
||||
[port_grpc]
|
||||
port = 50051
|
||||
ip = 127.0.0.1
|
||||
secure_gateway = 127.0.0.1
|
||||
|
||||
#[port_ws_public]
|
||||
#port = 6005
|
||||
|
||||
@@ -590,94 +590,6 @@ GRPCServerImpl::setupListeners()
|
||||
requests.push_back(std::move(callData));
|
||||
};
|
||||
|
||||
{
|
||||
using cd = CallData<
|
||||
org::xrpl::rpc::v1::GetFeeRequest,
|
||||
org::xrpl::rpc::v1::GetFeeResponse>;
|
||||
|
||||
addToRequests(std::make_shared<cd>(
|
||||
service_,
|
||||
*cq_,
|
||||
app_,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
|
||||
RequestGetFee,
|
||||
doFeeGrpc,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetFee,
|
||||
RPC::NEEDS_CURRENT_LEDGER,
|
||||
Resource::feeReferenceRPC,
|
||||
secureGatewayIPs_));
|
||||
}
|
||||
{
|
||||
using cd = CallData<
|
||||
org::xrpl::rpc::v1::GetAccountInfoRequest,
|
||||
org::xrpl::rpc::v1::GetAccountInfoResponse>;
|
||||
|
||||
addToRequests(std::make_shared<cd>(
|
||||
service_,
|
||||
*cq_,
|
||||
app_,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
|
||||
RequestGetAccountInfo,
|
||||
doAccountInfoGrpc,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetAccountInfo,
|
||||
RPC::NO_CONDITION,
|
||||
Resource::feeReferenceRPC,
|
||||
secureGatewayIPs_));
|
||||
}
|
||||
{
|
||||
using cd = CallData<
|
||||
org::xrpl::rpc::v1::GetTransactionRequest,
|
||||
org::xrpl::rpc::v1::GetTransactionResponse>;
|
||||
|
||||
addToRequests(std::make_shared<cd>(
|
||||
service_,
|
||||
*cq_,
|
||||
app_,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
|
||||
RequestGetTransaction,
|
||||
doTxGrpc,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::GetTransaction,
|
||||
RPC::NEEDS_NETWORK_CONNECTION,
|
||||
Resource::feeReferenceRPC,
|
||||
secureGatewayIPs_));
|
||||
}
|
||||
{
|
||||
using cd = CallData<
|
||||
org::xrpl::rpc::v1::SubmitTransactionRequest,
|
||||
org::xrpl::rpc::v1::SubmitTransactionResponse>;
|
||||
|
||||
addToRequests(std::make_shared<cd>(
|
||||
service_,
|
||||
*cq_,
|
||||
app_,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::AsyncService::
|
||||
RequestSubmitTransaction,
|
||||
doSubmitGrpc,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::SubmitTransaction,
|
||||
RPC::NEEDS_CURRENT_LEDGER,
|
||||
Resource::feeMediumBurdenRPC,
|
||||
secureGatewayIPs_));
|
||||
}
|
||||
|
||||
{
|
||||
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,
|
||||
&org::xrpl::rpc::v1::XRPLedgerAPIService::Stub::
|
||||
GetAccountTransactionHistory,
|
||||
RPC::NO_CONDITION,
|
||||
Resource::feeMediumBurdenRPC,
|
||||
secureGatewayIPs_));
|
||||
}
|
||||
|
||||
{
|
||||
using cd = CallData<
|
||||
org::xrpl::rpc::v1::GetLedgerRequest,
|
||||
|
||||
@@ -48,8 +48,6 @@ needCurrentOrClosed(Request& request)
|
||||
{
|
||||
// These are the only gRPC requests that specify a ledger
|
||||
if constexpr (
|
||||
std::is_same<Request, org::xrpl::rpc::v1::GetAccountInfoRequest>::
|
||||
value ||
|
||||
std::is_same<Request, org::xrpl::rpc::v1::GetLedgerRequest>::value ||
|
||||
std::is_same<Request, org::xrpl::rpc::v1::GetLedgerDataRequest>::
|
||||
value ||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
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,48 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package org.xrpl.rpc.v1;
|
||||
option java_package = "org.xrpl.rpc.v1";
|
||||
option java_multiple_files = true;
|
||||
|
||||
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.
|
||||
// Next field: 2
|
||||
message XRPDropsAmount
|
||||
{
|
||||
uint64 drops = 1 [jstype=JS_STRING];
|
||||
}
|
||||
|
||||
// A representation of an amount of issued currency.
|
||||
// Next field: 4
|
||||
message IssuedCurrencyAmount
|
||||
{
|
||||
// The currency used to value the amount.
|
||||
Currency currency = 1;
|
||||
|
||||
// The value of the amount. 8 bytes
|
||||
string value = 2;
|
||||
|
||||
// Unique account address of the entity issuing the currency.
|
||||
AccountAddress issuer = 3;
|
||||
}
|
||||
|
||||
// Next field: 3
|
||||
message Currency
|
||||
{
|
||||
// 3 character ASCII code
|
||||
string name = 1;
|
||||
|
||||
// 160 bit currency code. 20 bytes
|
||||
bytes code = 2;
|
||||
}
|
||||
@@ -1,606 +0,0 @@
|
||||
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 message 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 BurnedNFTokens
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
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 FirstLedgerSequence
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message LastLedgerSequence
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message LowQualityIn
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message LowQualityOut
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message MintedNFTokens
|
||||
{
|
||||
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 Ticket
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message TicketCount
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message TicketSequence
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message NFTokenTaxon
|
||||
{
|
||||
uint32 value = 1;
|
||||
}
|
||||
|
||||
message TransferFee
|
||||
{
|
||||
// is actually uint16
|
||||
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 NFTokenOfferNode
|
||||
{
|
||||
uint64 value = 1 [jstype=JS_STRING];
|
||||
}
|
||||
|
||||
message OwnerNode
|
||||
{
|
||||
uint64 value = 1 [jstype=JS_STRING];
|
||||
}
|
||||
|
||||
|
||||
// *** Messages wrapping 16 bytes ***
|
||||
|
||||
message EmailHash
|
||||
{
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message NFTokenID
|
||||
{
|
||||
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 NextPageMin
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message NFTokenBuyOffer
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message NFTokenSellOffer
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message PreviousPageMin
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message PreviousTransactionID
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message RootIndex
|
||||
{
|
||||
// 32 bytes
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message WalletLocator
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
message ValidatorToDisable
|
||||
{
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
message ValidatorToReEnable
|
||||
{
|
||||
bytes value = 1;
|
||||
}
|
||||
|
||||
// *** Messages wrapping a Currency value ***
|
||||
//
|
||||
// TODO: if there's a V2 of the API, fix this misspelling.
|
||||
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 NFTokenBrokerFee
|
||||
{
|
||||
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 Issuer
|
||||
{
|
||||
AccountAddress value = 1;
|
||||
}
|
||||
|
||||
message NFTokenMinter
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
message URI
|
||||
{
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
|
||||
// *** Aggregate type messages
|
||||
|
||||
// Next field: 3
|
||||
message NFToken
|
||||
{
|
||||
NFTokenID nftoken_id = 1;
|
||||
|
||||
URI uri = 2;
|
||||
}
|
||||
|
||||
// Next field: 3
|
||||
message SignerEntry
|
||||
{
|
||||
Account account = 1;
|
||||
|
||||
SignerWeight signer_weight = 2;
|
||||
|
||||
WalletLocator wallet_locator = 3;
|
||||
}
|
||||
|
||||
// Next field: 3
|
||||
message DisabledValidator
|
||||
{
|
||||
PublicKey public_key = 1;
|
||||
|
||||
FirstLedgerSequence ledger_sequence = 2;
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package org.xrpl.rpc.v1;
|
||||
option java_package = "org.xrpl.rpc.v1";
|
||||
option java_multiple_files = true;
|
||||
|
||||
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.
|
||||
// Next field: 6
|
||||
message GetAccountInfoRequest
|
||||
{
|
||||
// The address to get info about.
|
||||
AccountAddress account = 1;
|
||||
|
||||
bool strict = 2;
|
||||
|
||||
// Which ledger to use to retrieve data.
|
||||
// If this field is not set, the server will use the open ledger.
|
||||
// The open ledger includes data that is not validated or final.
|
||||
// To retrieve the most up to date and validated data, use
|
||||
// SHORTCUT_VALIDATED
|
||||
LedgerSpecifier ledger = 3;
|
||||
|
||||
bool queue = 4;
|
||||
|
||||
bool signer_lists = 5;
|
||||
|
||||
string client_ip = 6;
|
||||
}
|
||||
|
||||
// Response to GetAccountInfo RPC
|
||||
// Next field: 6
|
||||
message GetAccountInfoResponse
|
||||
{
|
||||
AccountRoot account_data = 1;
|
||||
|
||||
SignerList signer_list = 2;
|
||||
|
||||
uint32 ledger_index = 3;
|
||||
|
||||
QueueData queue_data = 4;
|
||||
|
||||
bool validated = 5;
|
||||
}
|
||||
|
||||
// Aggregate data about queued transactions
|
||||
// Next field: 11
|
||||
message QueueData
|
||||
{
|
||||
uint32 txn_count = 1;
|
||||
|
||||
bool auth_change_queued = 2;
|
||||
|
||||
uint32 lowest_sequence = 3;
|
||||
|
||||
uint32 highest_sequence = 4;
|
||||
|
||||
XRPDropsAmount max_spend_drops_total = 5;
|
||||
|
||||
repeated QueuedTransaction transactions = 6;
|
||||
|
||||
uint32 lowest_ticket = 7;
|
||||
|
||||
uint32 highest_ticket = 8;
|
||||
|
||||
uint32 sequence_count = 9;
|
||||
|
||||
uint32 ticket_count = 10;
|
||||
}
|
||||
|
||||
// Data about a single queued transaction
|
||||
// Next field: 8
|
||||
message QueuedTransaction
|
||||
{
|
||||
bool auth_change = 1;
|
||||
|
||||
XRPDropsAmount fee = 2;
|
||||
|
||||
uint64 fee_level = 3 [jstype=JS_STRING];
|
||||
|
||||
XRPDropsAmount max_spend_drops = 4;
|
||||
|
||||
Sequence sequence = 5;
|
||||
|
||||
LastLedgerSequence last_ledger_sequence = 6;
|
||||
|
||||
Ticket ticket = 7;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
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.
|
||||
// Note, this parameter acts as a filter, and can only reduce the number of
|
||||
// results. Specifying a single ledger will return only transactions from
|
||||
// that ledger. This includes specifying a ledger with a Shortcut. For
|
||||
// example, specifying SHORTCUT_VALIDATED will result in only transactions
|
||||
// that were part of the most recently validated ledger being returned.
|
||||
// Specifying a range of ledgers results in only transactions that were
|
||||
// included in a ledger within the specified range being returned.
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
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
|
||||
{
|
||||
string client_ip = 1;
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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;
|
||||
|
||||
// If the transaction was not found, server will report whether the entire
|
||||
// specified range was searched. The value is contained in the error message.
|
||||
// The error message is of the form:
|
||||
// "txn not found. searched_all = [true,false]"
|
||||
// If the transaction was found, this parameter is ignored.
|
||||
LedgerRange ledger_range = 3;
|
||||
|
||||
string client_ip = 4;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -25,16 +25,6 @@ message LedgerSpecifier
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
|
||||
// Next field: 3
|
||||
message RawLedgerObject
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
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: 17
|
||||
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;
|
||||
NFTokenOffer nftoken_offer = 15;
|
||||
NFTokenPage nftoken_page = 16;
|
||||
Offer offer = 9;
|
||||
PayChannel pay_channel = 10;
|
||||
RippleState ripple_state = 11;
|
||||
SignerList signer_list = 12;
|
||||
NegativeUNL negative_unl = 13;
|
||||
TicketObject ticket = 14;
|
||||
}
|
||||
}
|
||||
|
||||
// Next field: 15
|
||||
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;
|
||||
LEDGER_ENTRY_TYPE_NEGATIVE_UNL = 13;
|
||||
LEDGER_ENTRY_TYPE_TICKET = 14;
|
||||
LEDGER_ENTRY_TYPE_NFTOKEN_OFFER = 15;
|
||||
LEDGER_ENTRY_TYPE_NFTOKEN_PAGE = 16;
|
||||
}
|
||||
|
||||
// Next field: 19
|
||||
message AccountRoot
|
||||
{
|
||||
Account account = 1;
|
||||
|
||||
Balance balance = 2;
|
||||
|
||||
BurnedNFTokens burned_nftokens = 16;
|
||||
|
||||
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;
|
||||
|
||||
MintedNFTokens minted_nftokens = 17;
|
||||
|
||||
NFTokenMinter nftoken_minter = 18;
|
||||
|
||||
RegularKey regular_key = 12;
|
||||
|
||||
TickSize tick_size = 13;
|
||||
|
||||
TicketCount ticket_count = 15;
|
||||
|
||||
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: 12
|
||||
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;
|
||||
|
||||
NFTokenID nftoken_id = 11;
|
||||
}
|
||||
|
||||
// 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: 11
|
||||
message NFTokenOffer
|
||||
{
|
||||
Flags flags = 1;
|
||||
|
||||
Owner owner = 2;
|
||||
|
||||
NFTokenID nftoken_id = 3;
|
||||
|
||||
Amount amount = 4;
|
||||
|
||||
OwnerNode owner_node = 5;
|
||||
|
||||
NFTokenOfferNode nftoken_offer_node = 6;
|
||||
|
||||
Destination destination = 7;
|
||||
|
||||
Expiration expiration = 8;
|
||||
|
||||
PreviousTransactionID previous_transaction_id = 9;
|
||||
|
||||
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 10;
|
||||
}
|
||||
|
||||
// Next field: 7
|
||||
message NFTokenPage
|
||||
{
|
||||
Flags flags = 1;
|
||||
|
||||
PreviousPageMin previous_page_min = 2;
|
||||
|
||||
NextPageMin next_page_min = 3;
|
||||
|
||||
repeated NFToken nftokens = 4;
|
||||
|
||||
PreviousTransactionID previous_transaction_id = 5;
|
||||
|
||||
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 6;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
DestinationNode destination_node = 15;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Next field: 7
|
||||
message TicketObject
|
||||
{
|
||||
Flags flags = 1;
|
||||
|
||||
Account account = 2;
|
||||
|
||||
OwnerNode owner_node = 3;
|
||||
|
||||
PreviousTransactionID previous_transaction_id = 4;
|
||||
|
||||
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 5;
|
||||
|
||||
TicketSequence ticket_sequence = 6;
|
||||
}
|
||||
|
||||
// Next field: 5
|
||||
message NegativeUNL
|
||||
{
|
||||
repeated DisabledValidator disabled_validators = 1;
|
||||
|
||||
ValidatorToDisable validator_to_disable = 2;
|
||||
|
||||
ValidatorToReEnable validator_to_re_enable = 3;
|
||||
|
||||
Flags flags = 4;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package org.xrpl.rpc.v1;
|
||||
option java_package = "org.xrpl.rpc.v1";
|
||||
option java_multiple_files = true;
|
||||
|
||||
import "org/xrpl/rpc/v1/ledger_objects.proto";
|
||||
import "org/xrpl/rpc/v1/common.proto";
|
||||
|
||||
message SubmitMetadataRequest
|
||||
{
|
||||
|
||||
repeated AffectedNode affected_nodes = 1;
|
||||
uint32 ledger_sequence = 2;
|
||||
}
|
||||
|
||||
message SubmitMetadataResponse
|
||||
{
|
||||
bool success = 1;
|
||||
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
message PrepareLedgerRequest
|
||||
{
|
||||
uint32 ledger_index = 1;
|
||||
}
|
||||
|
||||
message PrepareLedgerResponse
|
||||
{
|
||||
bool success = 1;
|
||||
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
// Next field: 5
|
||||
message Meta
|
||||
{
|
||||
// index in ledger
|
||||
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;
|
||||
|
||||
DeliveredAmount delivered_amount = 4;
|
||||
}
|
||||
|
||||
// Next field: 3
|
||||
message TransactionResult
|
||||
{
|
||||
// Next field: 7
|
||||
enum ResultType
|
||||
{
|
||||
RESULT_TYPE_UNSPECIFIED = 0;
|
||||
// Claimed cost only
|
||||
RESULT_TYPE_TEC = 1;
|
||||
// Failure
|
||||
RESULT_TYPE_TEF = 2;
|
||||
// Local error
|
||||
RESULT_TYPE_TEL = 3;
|
||||
// Malformed transaction
|
||||
RESULT_TYPE_TEM = 4;
|
||||
// Retry
|
||||
RESULT_TYPE_TER = 5;
|
||||
// Success
|
||||
RESULT_TYPE_TES = 6;
|
||||
}
|
||||
|
||||
// category of the transaction result
|
||||
ResultType result_type = 1;
|
||||
|
||||
// full result string, i.e. tesSUCCESS
|
||||
string result = 2;
|
||||
}
|
||||
|
||||
// Next field: 6
|
||||
message AffectedNode
|
||||
{
|
||||
LedgerEntryType ledger_entry_type = 1;
|
||||
|
||||
// 32 bytes
|
||||
bytes ledger_index = 2;
|
||||
|
||||
oneof node
|
||||
{
|
||||
CreatedNode created_node = 3;
|
||||
DeletedNode deleted_node = 4;
|
||||
ModifiedNode modified_node = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Next field: 2
|
||||
message CreatedNode
|
||||
{
|
||||
LedgerObject new_fields = 1;
|
||||
}
|
||||
|
||||
// Next field: 2
|
||||
message DeletedNode
|
||||
{
|
||||
LedgerObject final_fields = 1;
|
||||
}
|
||||
|
||||
// Next field: 5
|
||||
message ModifiedNode {
|
||||
|
||||
LedgerObject final_fields = 1;
|
||||
|
||||
LedgerObject previous_fields = 2;
|
||||
|
||||
PreviousTransactionID previous_transaction_id = 3;
|
||||
|
||||
PreviousTransactionLedgerSequence previous_transaction_ledger_sequence = 4;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
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";
|
||||
|
||||
// A request to submit the signed transaction to the ledger.
|
||||
// Next field: 3
|
||||
message SubmitTransactionRequest
|
||||
{
|
||||
// The signed transaction to submit.
|
||||
bytes signed_transaction = 1;
|
||||
|
||||
bool fail_hard = 2;
|
||||
|
||||
string client_ip = 3;
|
||||
}
|
||||
|
||||
// A response when a signed transaction is submitted to the ledger.
|
||||
// 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.
|
||||
int64 engine_result_code = 2;
|
||||
|
||||
// Human-readable explanation of the transaction's preliminary result.
|
||||
string engine_result_message = 3;
|
||||
|
||||
// 32 bytes
|
||||
bytes hash = 4;
|
||||
}
|
||||
@@ -1,390 +0,0 @@
|
||||
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: 37
|
||||
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;
|
||||
|
||||
NFTokenAcceptOffer nftoken_accept_offer = 32;
|
||||
|
||||
NFTokenBurn nftoken_burn = 33;
|
||||
|
||||
NFTokenCancelOffer nftoken_cancel_offer = 34;
|
||||
|
||||
NFTokenCreateOffer nftoken_create_offer = 35;
|
||||
|
||||
NFTokenMint nftoken_mint = 36;
|
||||
|
||||
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;
|
||||
|
||||
TicketCreate ticket_create = 30;
|
||||
|
||||
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;
|
||||
|
||||
TicketSequence ticket_sequence = 31;
|
||||
}
|
||||
|
||||
// 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: 9
|
||||
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;
|
||||
|
||||
NFTokenMinter nftoken_minter = 8;
|
||||
}
|
||||
|
||||
// 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: 4
|
||||
message NFTokenAcceptOffer
|
||||
{
|
||||
NFTokenBrokerFee nftoken_broker_fee = 1;
|
||||
|
||||
NFTokenBuyOffer nftoken_buy_offer = 2;
|
||||
|
||||
NFTokenSellOffer nftoken_sell_offer = 3;
|
||||
}
|
||||
|
||||
// Next field: 3
|
||||
message NFTokenBurn
|
||||
{
|
||||
Owner owner = 1;
|
||||
|
||||
NFTokenID nftoken_id = 2;
|
||||
}
|
||||
|
||||
// Next field: 2
|
||||
message NFTokenCancelOffer
|
||||
{
|
||||
repeated Index nftoken_offers = 1;
|
||||
}
|
||||
|
||||
// Next field: 6
|
||||
message NFTokenCreateOffer
|
||||
{
|
||||
Amount amount = 1;
|
||||
|
||||
Destination destination = 2;
|
||||
|
||||
Expiration expiration = 3;
|
||||
|
||||
Owner owner = 4;
|
||||
|
||||
NFTokenID nftoken_id = 5;
|
||||
}
|
||||
|
||||
// Next field: 5
|
||||
message NFTokenMint
|
||||
{
|
||||
Issuer issuer = 1;
|
||||
|
||||
NFTokenTaxon nftoken_taxon = 2;
|
||||
|
||||
TransferFee transfer_fee = 3;
|
||||
|
||||
URI uri = 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: 2
|
||||
message TicketCreate
|
||||
{
|
||||
TicketCount count = 1;
|
||||
}
|
||||
|
||||
// Next field: 4
|
||||
message TrustSet
|
||||
{
|
||||
LimitAmount limit_amount = 1;
|
||||
|
||||
QualityIn quality_in = 2;
|
||||
|
||||
QualityOut quality_out = 3;
|
||||
}
|
||||
@@ -4,39 +4,18 @@ 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";
|
||||
import "org/xrpl/rpc/v1/get_ledger.proto";
|
||||
import "org/xrpl/rpc/v1/get_ledger_entry.proto";
|
||||
import "org/xrpl/rpc/v1/get_ledger_data.proto";
|
||||
import "org/xrpl/rpc/v1/get_ledger_diff.proto";
|
||||
|
||||
|
||||
// RPCs available to interact with the XRP Ledger.
|
||||
// The gRPC API mimics the JSON API. Refer to xrpl.org for documentation
|
||||
// These methods are binary only methods for retrieiving arbitrary ledger state
|
||||
// via gRPC. These methods are used by clio and reporting mode, but can also be
|
||||
// used by any client that wants to extract ledger state in an efficient manner.
|
||||
// They do not directly mimic the JSON equivalent methods.
|
||||
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);
|
||||
|
||||
// Get all validated transactions associated with a given account
|
||||
rpc GetAccountTransactionHistory(GetAccountTransactionHistoryRequest) returns (GetAccountTransactionHistoryResponse);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// The below methods do not mimic the JSON API exactly, and are mostly binary
|
||||
|
||||
// Get a specific ledger, optionally including transactions and any modified,
|
||||
// added or deleted ledger objects
|
||||
rpc GetLedger(GetLedgerRequest) returns (GetLedgerResponse);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <ripple/protocol/Protocol.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <org/xrpl/rpc/v1/amount.pb.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
@@ -34,28 +34,6 @@ namespace ripple {
|
||||
* the status will be sent to the client, and the response will be ommitted
|
||||
*/
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetAccountInfoResponse, grpc::Status>
|
||||
doAccountInfoGrpc(
|
||||
RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context);
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetFeeResponse, grpc::Status>
|
||||
doFeeGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetFeeRequest>& 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<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);
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status>
|
||||
doLedgerGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>& context);
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <grpc/status.h>
|
||||
|
||||
@@ -222,95 +221,4 @@ doAccountInfo(RPC::JsonContext& context)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetAccountInfoResponse, grpc::Status>
|
||||
doAccountInfoGrpc(
|
||||
RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>& context)
|
||||
{
|
||||
// Return values
|
||||
org::xrpl::rpc::v1::GetAccountInfoResponse result;
|
||||
grpc::Status status = grpc::Status::OK;
|
||||
|
||||
// input
|
||||
org::xrpl::rpc::v1::GetAccountInfoRequest& params = context.params;
|
||||
|
||||
// get ledger
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
auto lgrStatus = RPC::ledgerFromRequest(ledger, context);
|
||||
if (lgrStatus || !ledger)
|
||||
{
|
||||
grpc::Status errorStatus;
|
||||
if (lgrStatus.toErrorCode() == rpcINVALID_PARAMS)
|
||||
{
|
||||
errorStatus = grpc::Status(
|
||||
grpc::StatusCode::INVALID_ARGUMENT, lgrStatus.message());
|
||||
}
|
||||
else
|
||||
{
|
||||
errorStatus =
|
||||
grpc::Status(grpc::StatusCode::NOT_FOUND, lgrStatus.message());
|
||||
}
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
result.set_ledger_index(ledger->info().seq);
|
||||
result.set_validated(
|
||||
RPC::isValidated(context.ledgerMaster, *ledger, context.app));
|
||||
|
||||
// decode account
|
||||
AccountID accountID;
|
||||
std::string strIdent = params.account().address();
|
||||
error_code_i code =
|
||||
RPC::accountFromStringWithCode(accountID, strIdent, params.strict());
|
||||
if (code != rpcSUCCESS)
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT, "invalid account"};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
// get account data
|
||||
auto const sleAccepted = ledger->read(keylet::account(accountID));
|
||||
if (sleAccepted)
|
||||
{
|
||||
RPC::convert(*result.mutable_account_data(), *sleAccepted);
|
||||
|
||||
// signer lists
|
||||
if (params.signer_lists())
|
||||
{
|
||||
auto const sleSigners = ledger->read(keylet::signers(accountID));
|
||||
if (sleSigners)
|
||||
{
|
||||
org::xrpl::rpc::v1::SignerList& signerListProto =
|
||||
*result.mutable_signer_list();
|
||||
RPC::convert(signerListProto, *sleSigners);
|
||||
}
|
||||
}
|
||||
|
||||
// queued transactions
|
||||
if (params.queue())
|
||||
{
|
||||
if (!ledger->open())
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"requested queue but ledger is not open"};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
std::vector<TxQ::TxDetails> const txs =
|
||||
context.app.getTxQ().getAccountTxs(accountID);
|
||||
org::xrpl::rpc::v1::QueueData& queueData =
|
||||
*result.mutable_queue_data();
|
||||
RPC::convert(queueData, txs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::NOT_FOUND, "account not found"};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
return {result, status};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
#include <grpcpp/grpcpp.h>
|
||||
@@ -51,69 +50,6 @@ using AccountTxResult = RelationalDatabase::AccountTxResult;
|
||||
using LedgerShortcut = RelationalDatabase::LedgerShortcut;
|
||||
using LedgerSpecifier = RelationalDatabase::LedgerSpecifier;
|
||||
|
||||
// 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 (auto hash = uint256::fromVoidChecked(specifier.hash()))
|
||||
{
|
||||
ledger = *hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"ledger hash malformed"};
|
||||
return errorStatus;
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -331,131 +267,6 @@ doAccountTxHelp(RPC::Context& context, AccountTxArgs const& args)
|
||||
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 if (error.toErrorCode() == rpcNOT_SYNCED)
|
||||
{
|
||||
status = {grpc::StatusCode::FAILED_PRECONDITION, 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)
|
||||
{
|
||||
RPC::convert(*txnProto->mutable_meta(), txnMeta);
|
||||
if (!txnProto->meta().has_delivered_amount())
|
||||
{
|
||||
if (auto amt = getDeliveredAmount(
|
||||
context,
|
||||
txn->getSTransaction(),
|
||||
*txnMeta,
|
||||
txn->getLedger()))
|
||||
{
|
||||
RPC::convert(
|
||||
*txnProto->mutable_meta()
|
||||
->mutable_delivered_amount(),
|
||||
*amt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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,
|
||||
@@ -597,59 +408,4 @@ doAccountTxJson(RPC::JsonContext& context)
|
||||
return populateJsonResponse(res, args, context);
|
||||
}
|
||||
|
||||
std::pair<
|
||||
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse,
|
||||
grpc::Status>
|
||||
doAccountTxGrpc(
|
||||
RPC::GRPCContext<org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest>&
|
||||
context)
|
||||
{
|
||||
if (!context.app.config().useTxTables())
|
||||
{
|
||||
return {
|
||||
{},
|
||||
{grpc::StatusCode::UNIMPLEMENTED, "Not enabled in configuration."}};
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -38,49 +38,4 @@ doFee(RPC::JsonContext& context)
|
||||
return context.params;
|
||||
}
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetFeeResponse, grpc::Status>
|
||||
doFeeGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetFeeRequest>& context)
|
||||
{
|
||||
org::xrpl::rpc::v1::GetFeeResponse reply;
|
||||
grpc::Status status = grpc::Status::OK;
|
||||
|
||||
Application& app = context.app;
|
||||
auto const view = app.openLedger().current();
|
||||
if (!view)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
return {reply, status};
|
||||
}
|
||||
|
||||
auto const metrics = app.getTxQ().getMetrics(*view);
|
||||
|
||||
// current ledger data
|
||||
reply.set_current_ledger_size(metrics.txInLedger);
|
||||
reply.set_current_queue_size(metrics.txCount);
|
||||
reply.set_expected_ledger_size(metrics.txPerLedger);
|
||||
reply.set_ledger_current_index(view->info().seq);
|
||||
reply.set_max_queue_size(*metrics.txQMaxSize);
|
||||
|
||||
// fee levels data
|
||||
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
|
||||
org::xrpl::rpc::v1::Fee& fee = *reply.mutable_fee();
|
||||
auto const baseFee = view->fees().base;
|
||||
fee.mutable_base_fee()->set_drops(
|
||||
toDrops(metrics.referenceFeeLevel, baseFee).drops());
|
||||
fee.mutable_minimum_fee()->set_drops(
|
||||
toDrops(metrics.minProcessingFeeLevel, baseFee).drops());
|
||||
fee.mutable_median_fee()->set_drops(
|
||||
toDrops(metrics.medFeeLevel, baseFee).drops());
|
||||
|
||||
fee.mutable_open_ledger_fee()->set_drops(
|
||||
(toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee) + 1)
|
||||
.drops());
|
||||
return {reply, status};
|
||||
}
|
||||
} // namespace ripple
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
#include <ripple/rpc/handlers/LedgerHandler.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/impl/TransactionSign.h>
|
||||
|
||||
@@ -194,102 +193,4 @@ doSubmit(RPC::JsonContext& context)
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::SubmitTransactionResponse, grpc::Status>
|
||||
doSubmitGrpc(
|
||||
RPC::GRPCContext<org::xrpl::rpc::v1::SubmitTransactionRequest>& context)
|
||||
{
|
||||
// return values
|
||||
org::xrpl::rpc::v1::SubmitTransactionResponse result;
|
||||
grpc::Status status = grpc::Status::OK;
|
||||
|
||||
// input
|
||||
auto request = context.params;
|
||||
|
||||
std::string const& tx = request.signed_transaction();
|
||||
|
||||
// convert to blob
|
||||
Blob blob{tx.begin(), tx.end()};
|
||||
|
||||
// serialize
|
||||
SerialIter sitTrans(makeSlice(blob));
|
||||
std::shared_ptr<STTx const> stpTrans;
|
||||
try
|
||||
{
|
||||
stpTrans = std::make_shared<STTx const>(std::ref(sitTrans));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"invalid transaction: " + std::string(e.what())};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
// check validity
|
||||
{
|
||||
if (!context.app.checkSigs())
|
||||
forceValidity(
|
||||
context.app.getHashRouter(),
|
||||
stpTrans->getTransactionID(),
|
||||
Validity::SigGoodOnly);
|
||||
auto [validity, reason] = checkValidity(
|
||||
context.app.getHashRouter(),
|
||||
*stpTrans,
|
||||
context.ledgerMaster.getCurrentLedger()->rules(),
|
||||
context.app.config());
|
||||
if (validity != Validity::Valid)
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"invalid transaction: " + reason};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
auto tpTrans = std::make_shared<Transaction>(stpTrans, reason, context.app);
|
||||
if (tpTrans->getStatus() != NEW)
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"invalid transaction: " + reason};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto const failType = NetworkOPs::doFailHard(request.fail_hard());
|
||||
|
||||
// submit to network
|
||||
context.netOps.processTransaction(
|
||||
tpTrans, isUnlimited(context.role), true, failType);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT,
|
||||
"invalid transaction : " + std::string(e.what())};
|
||||
return {result, errorStatus};
|
||||
}
|
||||
|
||||
// return preliminary result
|
||||
if (temUNCERTAIN != tpTrans->getResult())
|
||||
{
|
||||
RPC::convert(*result.mutable_engine_result(), tpTrans->getResult());
|
||||
|
||||
std::string sToken;
|
||||
std::string sHuman;
|
||||
|
||||
transResultInfo(tpTrans->getResult(), sToken, sHuman);
|
||||
|
||||
result.mutable_engine_result()->set_result(sToken);
|
||||
result.set_engine_result_code(TERtoInt(tpTrans->getResult()));
|
||||
result.set_engine_result_message(sHuman);
|
||||
|
||||
uint256 hash = tpTrans->getID();
|
||||
result.set_hash(hash.data(), hash.size());
|
||||
}
|
||||
return {result, status};
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -251,101 +250,6 @@ doTxHelp(RPC::Context& context, TxArgs const& args)
|
||||
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 != TxSearched::unknown)
|
||||
{
|
||||
status = {
|
||||
grpc::StatusCode::NOT_FOUND,
|
||||
"txn not found. searched_all = " +
|
||||
to_string(
|
||||
(result.searchedAll == TxSearched::all ? "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,
|
||||
@@ -437,48 +341,4 @@ doTxJson(RPC::JsonContext& context)
|
||||
return populateJsonResponse(res, args, context);
|
||||
}
|
||||
|
||||
std::pair<org::xrpl::rpc::v1::GetTransactionResponse, grpc::Status>
|
||||
doTxGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetTransactionRequest>& context)
|
||||
{
|
||||
if (!context.app.config().useTxTables())
|
||||
{
|
||||
return {
|
||||
{},
|
||||
{grpc::StatusCode::UNIMPLEMENTED, "Not enabled in configuration."}};
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
if (auto hash = uint256::fromVoidChecked(request.hash()))
|
||||
{
|
||||
args.hash = *hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
grpc::Status errorStatus{
|
||||
grpc::StatusCode::INVALID_ARGUMENT, "tx 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/protocol/Protocol.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <ripple/protocol/TxMeta.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::vector<TxQ::TxDetails> 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);
|
||||
|
||||
void
|
||||
convert(org::xrpl::rpc::v1::NegativeUNL& 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
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
@@ -300,12 +299,6 @@ ledgerFromRequest(T& ledger, GRPCContext<R>& context)
|
||||
return ledgerFromSpecifier(ledger, request.ledger(), context);
|
||||
}
|
||||
|
||||
// explicit instantiation of above function
|
||||
template Status
|
||||
ledgerFromRequest<>(
|
||||
std::shared_ptr<ReadView const>&,
|
||||
GRPCContext<org::xrpl::rpc::v1::GetAccountInfoRequest>&);
|
||||
|
||||
// explicit instantiation of above function
|
||||
template Status
|
||||
ledgerFromRequest<>(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,6 @@
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/rpc/impl/GRPCHelpers.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -1883,114 +1882,6 @@ class NegativeUNLVoteFilterValidations_test : public beast::unit_test::suite
|
||||
}
|
||||
};
|
||||
|
||||
class NegativeUNLgRPC_test : public beast::unit_test::suite
|
||||
{
|
||||
template <class T>
|
||||
std::string
|
||||
toByteString(T const& data)
|
||||
{
|
||||
const char* bytes = reinterpret_cast<const char*>(data.data());
|
||||
return {bytes, data.size()};
|
||||
}
|
||||
|
||||
void
|
||||
testGRPC()
|
||||
{
|
||||
testcase("gRPC test");
|
||||
|
||||
auto gRpcTest = [this](
|
||||
std::uint32_t negUnlSize,
|
||||
bool hasToDisable,
|
||||
bool hasToReEnable) -> bool {
|
||||
NetworkHistory history = {
|
||||
*this, {20, negUnlSize, hasToDisable, hasToReEnable, {}}};
|
||||
if (!history.goodHistory)
|
||||
return false;
|
||||
|
||||
auto const& negUnlObject =
|
||||
history.lastLedger()->read(keylet::negativeUNL());
|
||||
if (!negUnlSize && !hasToDisable && !hasToReEnable && !negUnlObject)
|
||||
return true;
|
||||
if (!negUnlObject)
|
||||
return false;
|
||||
|
||||
org::xrpl::rpc::v1::NegativeUNL to;
|
||||
ripple::RPC::convert(to, *negUnlObject);
|
||||
if (!to.has_flags() ||
|
||||
to.flags().value() != negUnlObject->getFlags())
|
||||
return false;
|
||||
|
||||
bool goodSize = to.disabled_validators_size() == negUnlSize &&
|
||||
to.has_validator_to_disable() == hasToDisable &&
|
||||
to.has_validator_to_re_enable() == hasToReEnable;
|
||||
if (!goodSize)
|
||||
return false;
|
||||
|
||||
if (negUnlSize)
|
||||
{
|
||||
if (!negUnlObject->isFieldPresent(sfDisabledValidators))
|
||||
return false;
|
||||
auto const& nUnlData =
|
||||
negUnlObject->getFieldArray(sfDisabledValidators);
|
||||
if (nUnlData.size() != negUnlSize)
|
||||
return false;
|
||||
int idx = 0;
|
||||
for (auto const& n : nUnlData)
|
||||
{
|
||||
if (!n.isFieldPresent(sfPublicKey) ||
|
||||
!n.isFieldPresent(sfFirstLedgerSequence))
|
||||
return false;
|
||||
|
||||
if (!to.disabled_validators(idx).has_ledger_sequence() ||
|
||||
!to.disabled_validators(idx).has_public_key())
|
||||
return false;
|
||||
|
||||
if (to.disabled_validators(idx).public_key().value() !=
|
||||
toByteString(n.getFieldVL(sfPublicKey)))
|
||||
return false;
|
||||
|
||||
if (to.disabled_validators(idx).ledger_sequence().value() !=
|
||||
n.getFieldU32(sfFirstLedgerSequence))
|
||||
return false;
|
||||
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasToDisable)
|
||||
{
|
||||
if (!negUnlObject->isFieldPresent(sfValidatorToDisable))
|
||||
return false;
|
||||
if (to.validator_to_disable().value() !=
|
||||
toByteString(
|
||||
negUnlObject->getFieldVL(sfValidatorToDisable)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasToReEnable)
|
||||
{
|
||||
if (!negUnlObject->isFieldPresent(sfValidatorToReEnable))
|
||||
return false;
|
||||
if (to.validator_to_re_enable().value() !=
|
||||
toByteString(
|
||||
negUnlObject->getFieldVL(sfValidatorToReEnable)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
BEAST_EXPECT(gRpcTest(0, false, false));
|
||||
BEAST_EXPECT(gRpcTest(2, true, true));
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGRPC();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(NegativeUNL, ledger, ripple);
|
||||
BEAST_DEFINE_TESTSUITE(NegativeUNLNoAmendment, ledger, ripple);
|
||||
|
||||
@@ -2006,7 +1897,6 @@ BEAST_DEFINE_TESTSUITE_PRIO(
|
||||
1);
|
||||
BEAST_DEFINE_TESTSUITE(NegativeUNLVoteNewValidator, consensus, ripple);
|
||||
BEAST_DEFINE_TESTSUITE(NegativeUNLVoteFilterValidations, consensus, ripple);
|
||||
BEAST_DEFINE_TESTSUITE(NegativeUNLgRPC, ledger, ripple);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,975 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/safe_cast.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/InnerObjectFormats.h>
|
||||
#include <ripple/protocol/LedgerFormats.h>
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
|
||||
#include "org/xrpl/rpc/v1/ledger_objects.pb.h"
|
||||
#include "org/xrpl/rpc/v1/transaction.pb.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// This test suite uses the google::protobuf::Descriptor class to do runtime
|
||||
// reflection on our gRPC stuff. At the time of this writing documentation
|
||||
// for Descriptor could be found here:
|
||||
//
|
||||
// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor#Descriptor
|
||||
|
||||
class KnownFormatToGRPC_test : public beast::unit_test::suite
|
||||
{
|
||||
private:
|
||||
static constexpr auto fieldTYPE_UINT32 =
|
||||
google::protobuf::FieldDescriptor::Type::TYPE_UINT32;
|
||||
|
||||
static constexpr auto fieldTYPE_UINT64 =
|
||||
google::protobuf::FieldDescriptor::Type::TYPE_UINT64;
|
||||
|
||||
static constexpr auto fieldTYPE_BYTES =
|
||||
google::protobuf::FieldDescriptor::Type::TYPE_BYTES;
|
||||
|
||||
static constexpr auto fieldTYPE_STRING =
|
||||
google::protobuf::FieldDescriptor::Type::TYPE_STRING;
|
||||
|
||||
static constexpr auto fieldTYPE_MESSAGE =
|
||||
google::protobuf::FieldDescriptor::Type::TYPE_MESSAGE;
|
||||
|
||||
// Format names are CamelCase and FieldDescriptor names are snake_case.
|
||||
// Convert from CamelCase to snake_case. Do not be fooled by consecutive
|
||||
// capital letters like in NegativeUNL.
|
||||
static std::string
|
||||
formatNameToEntryTypeName(std::string const& fmtName)
|
||||
{
|
||||
std::string entryName;
|
||||
entryName.reserve(fmtName.size());
|
||||
bool prevUpper = false;
|
||||
for (std::size_t i = 0; i < fmtName.size(); i++)
|
||||
{
|
||||
char const ch = fmtName[i];
|
||||
bool const upper = std::isupper(ch);
|
||||
if (i > 0 && !prevUpper && upper)
|
||||
entryName.push_back('_');
|
||||
|
||||
prevUpper = upper;
|
||||
entryName.push_back(std::tolower(ch));
|
||||
}
|
||||
return entryName;
|
||||
};
|
||||
|
||||
// Create a map of (most) all the SFields in an SOTemplate. This map
|
||||
// can be used to correlate a gRPC Descriptor to its corresponding SField.
|
||||
template <typename KeyType>
|
||||
static std::map<std::string, SField const*>
|
||||
soTemplateToSFields(
|
||||
SOTemplate const& soTemplate,
|
||||
[[maybe_unused]] KeyType fmtId)
|
||||
{
|
||||
std::map<std::string, SField const*> sFields;
|
||||
for (SOElement const& element : soTemplate)
|
||||
{
|
||||
SField const& sField = element.sField();
|
||||
|
||||
// Fields that gRPC never includes.
|
||||
//
|
||||
// o sfLedgerIndex and
|
||||
// o sfLedgerEntryType are common to all ledger objects, so
|
||||
// gRPC includes them at a higher level than the ledger
|
||||
// object itself.
|
||||
//
|
||||
// o sfOperationLimit is an optional field in all transactions,
|
||||
// but no one knows what it was intended for.
|
||||
using FieldCode_t =
|
||||
std::remove_const<decltype(SField::fieldCode)>::type;
|
||||
static const std::set<FieldCode_t> excludedSFields{
|
||||
sfLedgerIndex.fieldCode,
|
||||
sfLedgerEntryType.fieldCode,
|
||||
sfOperationLimit.fieldCode};
|
||||
|
||||
if (excludedSFields.count(sField.fieldCode))
|
||||
continue;
|
||||
|
||||
// There are certain fields that gRPC never represents in
|
||||
// transactions. Exclude those.
|
||||
//
|
||||
// o sfPreviousTxnID is obsolete and was replaced by
|
||||
// sfAccountTxnID some time before November of 2014.
|
||||
//
|
||||
// o sfWalletLocator and
|
||||
// o sfWalletSize have been deprecated for six years or more.
|
||||
//
|
||||
// o sfTransactionType is not needed by gRPC, since the typing
|
||||
// is handled using protobuf message types.
|
||||
if constexpr (std::is_same_v<KeyType, TxType>)
|
||||
{
|
||||
static const std::set<FieldCode_t> excludedTxFields{
|
||||
sfPreviousTxnID.fieldCode,
|
||||
sfTransactionType.fieldCode,
|
||||
sfWalletLocator.fieldCode,
|
||||
sfWalletSize.fieldCode};
|
||||
|
||||
if (excludedTxFields.count(sField.fieldCode))
|
||||
continue;
|
||||
}
|
||||
|
||||
// If fmtId is a LedgerEntryType, exclude certain fields.
|
||||
if constexpr (std::is_same_v<KeyType, LedgerEntryType>)
|
||||
{
|
||||
// Fields that gRPC does not include in certain LedgerFormats.
|
||||
//
|
||||
// o sfWalletLocator,
|
||||
// o sfWalletSize,
|
||||
// o sfExchangeRate, and
|
||||
// o sfFirstLedgerSequence are all deprecated fields in
|
||||
// their respective ledger objects.
|
||||
static const std::
|
||||
map<LedgerEntryType, std::vector<SField const*>>
|
||||
gRPCOmitFields{
|
||||
{ltACCOUNT_ROOT, {&sfWalletLocator, &sfWalletSize}},
|
||||
{ltDIR_NODE, {&sfExchangeRate}},
|
||||
{ltLEDGER_HASHES, {&sfFirstLedgerSequence}},
|
||||
};
|
||||
|
||||
if (auto const iter = gRPCOmitFields.find(fmtId);
|
||||
iter != gRPCOmitFields.end())
|
||||
{
|
||||
std::vector<SField const*> const& omits = iter->second;
|
||||
|
||||
// Check for fields that gRPC omits from this type.
|
||||
if (std::find_if(
|
||||
omits.begin(),
|
||||
omits.end(),
|
||||
[&sField](SField const* const omit) {
|
||||
return *omit == sField;
|
||||
}) != omits.end())
|
||||
{
|
||||
// This is one of the fields that gRPC omits.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The SFields and gRPC disagree on the names of some fields.
|
||||
// Provide a mapping from SField names to gRPC names for the
|
||||
// known exceptions.
|
||||
//
|
||||
// clang-format off
|
||||
//
|
||||
// The implementers of the gRPC interface made the decision not
|
||||
// to abbreviate anything. This accounts for the following
|
||||
// field name differences:
|
||||
//
|
||||
// "AccountTxnID", "AccountTransactionID"
|
||||
// "PreviousTxnID", "PreviousTransactionID"
|
||||
// "PreviousTxnLgrSeq", "PreviousTransactionLedgerSequence"
|
||||
// "SigningPubKey", "SigningPublicKey"
|
||||
// "TxnSignature", "TransactionSignature"
|
||||
//
|
||||
// gRPC adds typing information for Fee, which accounts for
|
||||
// "Fee", "XRPDropsAmount"
|
||||
//
|
||||
// There's one misspelling which accounts for
|
||||
// "TakerGetsCurrency", "TakerGetsCurreny"
|
||||
//
|
||||
// The implementers of the gRPC interface observed that a
|
||||
// PaymentChannelClaim transaction has a TxnSignature field at the
|
||||
// upper level and a Signature field at the lever level. They
|
||||
// felt that was confusing, which is the reason for
|
||||
// "Signature", "PaymentChannelSignature"
|
||||
//
|
||||
static const std::map<std::string, std::string> sFieldToGRPC{
|
||||
{"AccountTxnID", "AccountTransactionID"},
|
||||
{"Fee", "XRPDropsAmount"},
|
||||
{"PreviousTxnID", "PreviousTransactionID"},
|
||||
{"PreviousTxnLgrSeq", "PreviousTransactionLedgerSequence"},
|
||||
{"Signature", "PaymentChannelSignature"},
|
||||
{"SigningPubKey", "SigningPublicKey"},
|
||||
{"TakerGetsCurrency", "TakerGetsCurreny"},
|
||||
{"TxnSignature", "TransactionSignature"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
auto const iter = sFieldToGRPC.find(sField.getName());
|
||||
std::string gRPCName =
|
||||
iter != sFieldToGRPC.end() ? iter->second : sField.getName();
|
||||
|
||||
sFields.insert({std::move(gRPCName), &sField});
|
||||
}
|
||||
return sFields;
|
||||
}
|
||||
|
||||
// Given a Descriptor for a KnownFormat and a map of the SFields of that
|
||||
// KnownFormat, make sure the fields are aligned.
|
||||
void
|
||||
validateDescriptorAgainstSFields(
|
||||
google::protobuf::Descriptor const* const pbufDescriptor,
|
||||
google::protobuf::Descriptor const* const commonFields,
|
||||
std::string const& knownFormatName,
|
||||
std::map<std::string, SField const*>&& sFields)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
// We'll be running through two sets of pbuf::Descriptors: the ones in
|
||||
// the OneOf and the common fields. Here is a lambda that factors out
|
||||
// the common checking code for these two cases.
|
||||
auto checkFieldDesc = [this, &sFields, &knownFormatName](
|
||||
pbuf::FieldDescriptor const* const
|
||||
fieldDesc) {
|
||||
// gRPC has different handling for repeated vs non-repeated
|
||||
// types. So we need to do that too.
|
||||
std::string name;
|
||||
if (fieldDesc->is_repeated())
|
||||
{
|
||||
// Repeated-type handling.
|
||||
|
||||
// Munge the fieldDescriptor name so it looks like the
|
||||
// name in sFields.
|
||||
name = fieldDesc->camelcase_name();
|
||||
name[0] = toupper(name[0]);
|
||||
|
||||
// The ledger gives UNL all caps. Adapt to that.
|
||||
if (size_t const i = name.find("Unl"); i != std::string::npos)
|
||||
{
|
||||
name[i + 1] = 'N';
|
||||
name[i + 2] = 'L';
|
||||
}
|
||||
|
||||
// The ledger gives the NFT part of NFToken all caps.
|
||||
// Adapt to that.
|
||||
if (size_t const i = name.find("Nft"); i != std::string::npos)
|
||||
{
|
||||
name[i + 1] = 'F';
|
||||
name[i + 2] = 'T';
|
||||
}
|
||||
|
||||
if (!sFields.count(name))
|
||||
{
|
||||
fail(
|
||||
std::string("Repeated Protobuf Descriptor '") + name +
|
||||
"' expected in KnownFormat '" + knownFormatName +
|
||||
"' and not found",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
|
||||
validateRepeatedField(fieldDesc, sFields.at(name));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-repeated handling.
|
||||
pbuf::Descriptor const* const entryDesc =
|
||||
fieldDesc->message_type();
|
||||
if (entryDesc == nullptr)
|
||||
return;
|
||||
|
||||
name = entryDesc->name();
|
||||
if (!sFields.count(name))
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") +
|
||||
entryDesc->name() + "' expected in KnownFormat '" +
|
||||
knownFormatName + "' and not found",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
|
||||
validateDescriptor(entryDesc, sFields.at(entryDesc->name()));
|
||||
}
|
||||
// Remove the validated field from the map so we can tell if
|
||||
// there are left over fields at the end of all comparisons.
|
||||
sFields.erase(name);
|
||||
};
|
||||
|
||||
// Compare the SFields to the FieldDescriptor->Descriptors.
|
||||
for (int i = 0; i < pbufDescriptor->field_count(); ++i)
|
||||
{
|
||||
pbuf::FieldDescriptor const* const fieldDesc =
|
||||
pbufDescriptor->field(i);
|
||||
if (fieldDesc == nullptr || fieldDesc->type() != fieldTYPE_MESSAGE)
|
||||
continue;
|
||||
|
||||
checkFieldDesc(fieldDesc);
|
||||
}
|
||||
|
||||
// Now all of the OneOf-specific fields have been removed from
|
||||
// sFields. But there may be common fields left in there. Process
|
||||
// the commonFields next.
|
||||
if (commonFields)
|
||||
{
|
||||
for (int i = 0; i < commonFields->field_count(); ++i)
|
||||
{
|
||||
// If the field we picked up is a OneOf, skip it. Common
|
||||
// fields are never OneOfs.
|
||||
pbuf::FieldDescriptor const* const fieldDesc =
|
||||
commonFields->field(i);
|
||||
|
||||
if (fieldDesc == nullptr ||
|
||||
fieldDesc->containing_oneof() != nullptr ||
|
||||
fieldDesc->type() != fieldTYPE_MESSAGE)
|
||||
continue;
|
||||
|
||||
checkFieldDesc(fieldDesc);
|
||||
}
|
||||
}
|
||||
|
||||
// All SFields in the KnownFormat have corresponding gRPC fields
|
||||
// if the sFields map is now empty.
|
||||
if (!sFields.empty())
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + pbufDescriptor->name() +
|
||||
"' did not account for all fields in KnownFormat '" +
|
||||
knownFormatName + "'. Left over field: `" +
|
||||
sFields.begin()->first + "'",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
}
|
||||
|
||||
// Compare a protobuf descriptor with multiple oneOfFields to choose from
|
||||
// to an SField.
|
||||
void
|
||||
validateOneOfDescriptor(
|
||||
google::protobuf::Descriptor const* const entryDesc,
|
||||
SField const* const sField)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
// Note that it's not okay to compare names because SFields and
|
||||
// gRPC do not always agree on the names.
|
||||
if (entryDesc->field_count() == 0 || entryDesc->oneof_decl_count() != 1)
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + entryDesc->name() +
|
||||
"' expected to have multiple OneOf fields and nothing else",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
pbuf::FieldDescriptor const* const fieldDesc = entryDesc->field(0);
|
||||
if (fieldDesc == nullptr)
|
||||
{
|
||||
fail(
|
||||
std::string("Internal test failure. Unhandled nullptr "
|
||||
"in FieldDescriptor for '") +
|
||||
entryDesc->name() + "'",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling for CurrencyAmount
|
||||
if (sField->fieldType == STI_AMOUNT &&
|
||||
entryDesc->name() == "CurrencyAmount")
|
||||
{
|
||||
// SFields of type STI_AMOUNT are represented in gRPC by a
|
||||
// multi-field CurrencyAmount. We don't really learn anything
|
||||
// by diving into the interior of CurrencyAmount, so we stop here
|
||||
// and call it good.
|
||||
pass();
|
||||
return;
|
||||
}
|
||||
|
||||
fail(
|
||||
std::string("Unhandled OneOf Protobuf Descriptor '") +
|
||||
entryDesc->name() + "'",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
void
|
||||
validateMultiFieldDescriptor(
|
||||
google::protobuf::Descriptor const* const entryDesc,
|
||||
SField const* const sField)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
if (entryDesc->field_count() <= 1 || entryDesc->oneof_decl_count() != 0)
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + entryDesc->name() +
|
||||
"' expected to have multiple fields and nothing else",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// There are composite fields that the SFields handle differently
|
||||
// from gRPC. Handle those here.
|
||||
{
|
||||
struct FieldContents
|
||||
{
|
||||
std::string_view fieldName;
|
||||
google::protobuf::FieldDescriptor::Type fieldType;
|
||||
|
||||
bool
|
||||
operator<(FieldContents const& other) const
|
||||
{
|
||||
return this->fieldName < other.fieldName;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(FieldContents const& other) const
|
||||
{
|
||||
return this->fieldName == other.fieldName &&
|
||||
this->fieldType == other.fieldType;
|
||||
}
|
||||
};
|
||||
|
||||
struct SpecialEntry
|
||||
{
|
||||
std::string_view const descriptorName;
|
||||
SerializedTypeID const sFieldType;
|
||||
std::set<FieldContents> const fields;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
static const std::array specialEntries{
|
||||
SpecialEntry{
|
||||
"Currency", STI_UINT160,
|
||||
{
|
||||
{"name", fieldTYPE_STRING},
|
||||
{"code", fieldTYPE_BYTES}
|
||||
}
|
||||
},
|
||||
SpecialEntry{
|
||||
"Memo", STI_OBJECT,
|
||||
{
|
||||
{"memo_data", fieldTYPE_BYTES},
|
||||
{"memo_format", fieldTYPE_BYTES},
|
||||
{"memo_type", fieldTYPE_BYTES}
|
||||
}
|
||||
}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// If we're handling a SpecialEntry...
|
||||
if (auto const iter = std::find_if(
|
||||
specialEntries.begin(),
|
||||
specialEntries.end(),
|
||||
[entryDesc, sField](SpecialEntry const& entry) {
|
||||
return entryDesc->name() == entry.descriptorName &&
|
||||
sField->fieldType == entry.sFieldType;
|
||||
});
|
||||
iter != specialEntries.end())
|
||||
{
|
||||
// Verify the SField.
|
||||
if (!BEAST_EXPECT(sField->fieldType == iter->sFieldType))
|
||||
return;
|
||||
|
||||
// Verify all of the fields in the entryDesc.
|
||||
if (!BEAST_EXPECT(
|
||||
entryDesc->field_count() == iter->fields.size()))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < entryDesc->field_count(); ++i)
|
||||
{
|
||||
pbuf::FieldDescriptor const* const fieldDesc =
|
||||
entryDesc->field(i);
|
||||
|
||||
FieldContents const contents{
|
||||
fieldDesc->name(), fieldDesc->type()};
|
||||
|
||||
if (!BEAST_EXPECT(
|
||||
iter->fields.find(contents) != iter->fields.end()))
|
||||
return;
|
||||
}
|
||||
|
||||
// This field is good.
|
||||
pass();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the field was not one of the SpecialEntries, we expect it to be
|
||||
// an InnerObjectFormat.
|
||||
SOTemplate const* const innerFormat =
|
||||
InnerObjectFormats::getInstance().findSOTemplateBySField(*sField);
|
||||
if (innerFormat == nullptr)
|
||||
{
|
||||
fail(
|
||||
"SOTemplate for field '" + sField->getName() + "' not found",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map we can use use to correlate each field in the
|
||||
// gRPC Descriptor to its corresponding SField.
|
||||
std::map<std::string, SField const*> sFields =
|
||||
soTemplateToSFields(*innerFormat, 0);
|
||||
|
||||
// Compare the SFields to the FieldDescriptor->Descriptors.
|
||||
validateDescriptorAgainstSFields(
|
||||
entryDesc, nullptr, sField->getName(), std::move(sFields));
|
||||
}
|
||||
|
||||
// Compare a protobuf descriptor with only one field to an SField.
|
||||
void
|
||||
validateOneDescriptor(
|
||||
google::protobuf::Descriptor const* const entryDesc,
|
||||
SField const* const sField)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
// Note that it's not okay to compare names because SFields and
|
||||
// gRPC do not always agree on the names.
|
||||
if (entryDesc->field_count() != 1 || entryDesc->oneof_decl_count() != 0)
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + entryDesc->name() +
|
||||
"' expected to be one field and nothing else",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
pbuf::FieldDescriptor const* const fieldDesc = entryDesc->field(0);
|
||||
if (fieldDesc == nullptr)
|
||||
{
|
||||
fail(
|
||||
std::string("Internal test failure. Unhandled nullptr "
|
||||
"in FieldDescriptor for '") +
|
||||
entryDesc->name() + "'",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map from SerializedTypeID to pbuf::FieldDescriptor::Type.
|
||||
//
|
||||
// This works for most, but not all, types because of divergence
|
||||
// between the gRPC and LedgerFormat implementations. We deal
|
||||
// with the special cases later.
|
||||
// clang-format off
|
||||
static const std::map<SerializedTypeID, pbuf::FieldDescriptor::Type>
|
||||
sTypeToFieldDescType{
|
||||
{STI_UINT8, fieldTYPE_UINT32},
|
||||
{STI_UINT16, fieldTYPE_UINT32},
|
||||
{STI_UINT32, fieldTYPE_UINT32},
|
||||
|
||||
{STI_UINT64, fieldTYPE_UINT64},
|
||||
|
||||
{STI_ACCOUNT, fieldTYPE_STRING},
|
||||
|
||||
{STI_AMOUNT, fieldTYPE_BYTES},
|
||||
{STI_UINT128, fieldTYPE_BYTES},
|
||||
{STI_UINT160, fieldTYPE_BYTES},
|
||||
{STI_UINT256, fieldTYPE_BYTES},
|
||||
{STI_VL, fieldTYPE_BYTES},
|
||||
};
|
||||
//clang-format on
|
||||
|
||||
// If the SField and FieldDescriptor::Type correlate we're good.
|
||||
if (auto const iter = sTypeToFieldDescType.find(sField->fieldType);
|
||||
iter != sTypeToFieldDescType.end() &&
|
||||
iter->second == fieldDesc->type())
|
||||
{
|
||||
pass();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle special cases for specific SFields.
|
||||
static const std::map<int, pbuf::FieldDescriptor::Type>
|
||||
sFieldCodeToFieldDescType{
|
||||
{sfDomain.fieldCode, fieldTYPE_STRING},
|
||||
{sfFee.fieldCode, fieldTYPE_UINT64},
|
||||
{sfURI.fieldCode, fieldTYPE_STRING}};
|
||||
|
||||
if (auto const iter = sFieldCodeToFieldDescType.find(sField->fieldCode);
|
||||
iter != sFieldCodeToFieldDescType.end() &&
|
||||
iter->second == fieldDesc->type())
|
||||
{
|
||||
pass();
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling for all Message types.
|
||||
if (fieldDesc->type() == fieldTYPE_MESSAGE)
|
||||
{
|
||||
// We need to recurse to get to the bottom of the field(s)
|
||||
// in question.
|
||||
|
||||
// Start by identifying which fields we need to be handling.
|
||||
// clang-format off
|
||||
static const std::map<int, std::string> messageMap{
|
||||
{sfAccount.fieldCode, "AccountAddress"},
|
||||
{sfAmount.fieldCode, "CurrencyAmount"},
|
||||
{sfAuthorize.fieldCode, "AccountAddress"},
|
||||
{sfBalance.fieldCode, "CurrencyAmount"},
|
||||
{sfDestination.fieldCode, "AccountAddress"},
|
||||
{sfFee.fieldCode, "XRPDropsAmount"},
|
||||
{sfHighLimit.fieldCode, "CurrencyAmount"},
|
||||
{sfLowLimit.fieldCode, "CurrencyAmount"},
|
||||
{sfOwner.fieldCode, "AccountAddress"},
|
||||
{sfRegularKey.fieldCode, "AccountAddress"},
|
||||
{sfSendMax.fieldCode, "CurrencyAmount"},
|
||||
{sfTakerGets.fieldCode, "CurrencyAmount"},
|
||||
{sfTakerGetsCurrency.fieldCode, "Currency"},
|
||||
{sfTakerPays.fieldCode, "CurrencyAmount"},
|
||||
{sfTakerPaysCurrency.fieldCode, "Currency"},
|
||||
};
|
||||
// clang-format on
|
||||
if (messageMap.count(sField->fieldCode))
|
||||
{
|
||||
pbuf::Descriptor const* const entry2Desc =
|
||||
fieldDesc->message_type();
|
||||
|
||||
if (entry2Desc == nullptr)
|
||||
{
|
||||
fail(
|
||||
std::string("Unexpected gRPC. ") + fieldDesc->name() +
|
||||
" MESSAGE with null Descriptor",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// The Descriptor name should match the messageMap name.
|
||||
if (messageMap.at(sField->fieldCode) != entry2Desc->name())
|
||||
{
|
||||
fail(
|
||||
std::string(
|
||||
"Internal test error. Mismatch between SField '") +
|
||||
sField->getName() + "' and gRPC Descriptor name '" +
|
||||
entry2Desc->name() + "'",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
|
||||
// Recurse to the next lower Descriptor.
|
||||
validateDescriptor(entry2Desc, sField);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fail(
|
||||
std::string("Internal test error. Unhandled FieldDescriptor '") +
|
||||
entryDesc->name() + "' has type `" + fieldDesc->type_name() +
|
||||
"` and label " + std::to_string(fieldDesc->label()),
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
// Compare a repeated protobuf FieldDescriptor to an SField.
|
||||
void
|
||||
validateRepeatedField(
|
||||
google::protobuf::FieldDescriptor const* const fieldDesc,
|
||||
SField const* const sField)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
pbuf::Descriptor const* const entryDesc = fieldDesc->message_type();
|
||||
if (entryDesc == nullptr)
|
||||
{
|
||||
fail(
|
||||
std::string("Expected Descriptor for repeated type ") +
|
||||
sField->getName(),
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// The following repeated types provide no further structure for their
|
||||
// in-ledger representation. We just have to trust that the gRPC
|
||||
// representation is reasonable for what the ledger implements.
|
||||
static const std::set<std::string> noFurtherDetail{
|
||||
{sfPaths.getName()},
|
||||
};
|
||||
|
||||
if (noFurtherDetail.count(sField->getName()))
|
||||
{
|
||||
// There is no Format representation for further details of this
|
||||
// repeated type. We've done the best we can.
|
||||
pass();
|
||||
return;
|
||||
}
|
||||
|
||||
// All of the repeated types that the test currently supports.
|
||||
static const std::map<std::string, SField const*> repeatsWhat{
|
||||
{sfAmendments.getName(), &sfAmendment},
|
||||
{sfDisabledValidators.getName(), &sfDisabledValidator},
|
||||
{sfHashes.getName(), &sfLedgerHash},
|
||||
{sfIndexes.getName(), &sfLedgerIndex},
|
||||
{sfMajorities.getName(), &sfMajority},
|
||||
{sfMemos.getName(), &sfMemo},
|
||||
{sfNFTokens.getName(), &sfNFToken},
|
||||
{sfSignerEntries.getName(), &sfSignerEntry},
|
||||
{sfSigners.getName(), &sfSigner},
|
||||
{sfNFTokenOffers.getName(), &sfLedgerIndex}};
|
||||
|
||||
if (!repeatsWhat.count(sField->getName()))
|
||||
{
|
||||
fail(
|
||||
std::string("Unexpected repeated type ") + fieldDesc->name(),
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
|
||||
// Process the type contained by the repeated type.
|
||||
validateDescriptor(entryDesc, repeatsWhat.at(sField->getName()));
|
||||
}
|
||||
|
||||
// Determine which of the Descriptor validators to dispatch to.
|
||||
void
|
||||
validateDescriptor(
|
||||
google::protobuf::Descriptor const* const entryDesc,
|
||||
SField const* const sField)
|
||||
{
|
||||
if (entryDesc->nested_type_count() != 0 ||
|
||||
entryDesc->enum_type_count() != 0 ||
|
||||
entryDesc->extension_range_count() != 0 ||
|
||||
entryDesc->reserved_range_count() != 0)
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + entryDesc->name() +
|
||||
"' uses unsupported protobuf features",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch to the correct validator
|
||||
if (entryDesc->oneof_decl_count() > 0)
|
||||
return validateOneOfDescriptor(entryDesc, sField);
|
||||
|
||||
if (entryDesc->field_count() > 1)
|
||||
return validateMultiFieldDescriptor(entryDesc, sField);
|
||||
|
||||
return validateOneDescriptor(entryDesc, sField);
|
||||
}
|
||||
|
||||
// Compare a protobuf descriptor to a KnownFormat::Item
|
||||
template <typename FmtType, typename FmtName>
|
||||
void
|
||||
validateFields(
|
||||
google::protobuf::Descriptor const* const pbufDescriptor,
|
||||
google::protobuf::Descriptor const* const commonFields,
|
||||
typename KnownFormats<FmtType, FmtName>::Item const* const
|
||||
knownFormatItem)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
// The names should usually be the same, but the bpufDescriptor
|
||||
// name might have "Object" appended.
|
||||
if (knownFormatItem->getName() != pbufDescriptor->name() &&
|
||||
knownFormatItem->getName() + "Object" != pbufDescriptor->name())
|
||||
{
|
||||
fail(
|
||||
std::string("Protobuf Descriptor '") + pbufDescriptor->name() +
|
||||
"' and KnownFormat::Item '" + knownFormatItem->getName() +
|
||||
"' don't have the same name",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
|
||||
// Create a map we can use use to correlate each field in the
|
||||
// gRPC Descriptor to its corresponding SField.
|
||||
std::map<std::string, SField const*> sFields = soTemplateToSFields(
|
||||
knownFormatItem->getSOTemplate(), knownFormatItem->getType());
|
||||
|
||||
// Compare the SFields to the FieldDescriptor->Descriptors.
|
||||
validateDescriptorAgainstSFields(
|
||||
pbufDescriptor,
|
||||
commonFields,
|
||||
knownFormatItem->getName(),
|
||||
std::move(sFields));
|
||||
}
|
||||
|
||||
template <typename FmtType, typename FmtName>
|
||||
void
|
||||
testKnownFormats(
|
||||
KnownFormats<FmtType, FmtName> const& knownFormat,
|
||||
std::string const& knownFormatName,
|
||||
google::protobuf::Descriptor const* const commonFields,
|
||||
google::protobuf::OneofDescriptor const* const oneOfDesc)
|
||||
{
|
||||
// Create namespace aliases for shorter names.
|
||||
namespace grpc = org::xrpl::rpc::v1;
|
||||
namespace pbuf = google::protobuf;
|
||||
|
||||
if (!BEAST_EXPECT(oneOfDesc != nullptr))
|
||||
return;
|
||||
|
||||
// Get corresponding names for all KnownFormat Items.
|
||||
std::map<
|
||||
std::string,
|
||||
typename KnownFormats<FmtType, FmtName>::Item const*>
|
||||
formatTypes;
|
||||
|
||||
for (auto const& item : knownFormat)
|
||||
{
|
||||
if constexpr (std::is_same_v<FmtType, LedgerEntryType>)
|
||||
{
|
||||
// Skip LedgerEntryTypes that gRPC does not currently support.
|
||||
static constexpr std::array<LedgerEntryType, 0> notSupported{};
|
||||
|
||||
if (std::find(
|
||||
notSupported.begin(),
|
||||
notSupported.end(),
|
||||
item.getType()) != notSupported.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<FmtType, TxType>)
|
||||
{
|
||||
// Skip TxTypes that gRPC does not currently support.
|
||||
static constexpr std::array notSupported{
|
||||
ttAMENDMENT, ttFEE, ttUNL_MODIFY};
|
||||
|
||||
if (std::find(
|
||||
notSupported.begin(),
|
||||
notSupported.end(),
|
||||
item.getType()) != notSupported.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
formatTypes
|
||||
.insert({formatNameToEntryTypeName(item.getName()), &item})
|
||||
.second == true);
|
||||
}
|
||||
|
||||
// Verify that the OneOf objects match. Start by comparing
|
||||
// KnownFormat vs gRPC OneOf counts.
|
||||
{
|
||||
BEAST_EXPECT(formatTypes.size() == oneOfDesc->field_count());
|
||||
}
|
||||
|
||||
// This loop
|
||||
// 1. Iterates through the gRPC OneOfs,
|
||||
// 2. Finds each gRPC OneOf's matching KnownFormat::Item,
|
||||
// 3. Sanity checks that the fields of the objects align well.
|
||||
for (auto i = 0; i < oneOfDesc->field_count(); ++i)
|
||||
{
|
||||
pbuf::FieldDescriptor const* const fieldDesc = oneOfDesc->field(i);
|
||||
|
||||
// The Field should be a TYPE_MESSAGE, which means we can get its
|
||||
// descriptor.
|
||||
if (fieldDesc->type() != fieldTYPE_MESSAGE)
|
||||
{
|
||||
fail(
|
||||
std::string("gRPC OneOf '") + fieldDesc->name() +
|
||||
"' is not TYPE_MESSAGE",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const fmtIter = formatTypes.find(fieldDesc->name());
|
||||
|
||||
if (fmtIter == formatTypes.cend())
|
||||
{
|
||||
fail(
|
||||
std::string("gRPC OneOf '") + fieldDesc->name() +
|
||||
"' not found in " + knownFormatName,
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate that the gRPC and KnownFormat fields align.
|
||||
validateFields<FmtType, FmtName>(
|
||||
fieldDesc->message_type(), commonFields, fmtIter->second);
|
||||
|
||||
// Remove the checked KnownFormat from the map. This way we
|
||||
// can check for leftovers when we're done processing.
|
||||
formatTypes.erase(fieldDesc->name());
|
||||
}
|
||||
|
||||
// Report any KnownFormats that don't have gRPC OneOfs.
|
||||
for (auto const& spare : formatTypes)
|
||||
{
|
||||
fail(
|
||||
knownFormatName + " '" + spare.second->getName() +
|
||||
"' does not have a corresponding gRPC OneOf",
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
testLedgerObjectGRPCOneOfs()
|
||||
{
|
||||
testcase("Ledger object validation");
|
||||
|
||||
org::xrpl::rpc::v1::LedgerObject const ledgerObject;
|
||||
|
||||
testKnownFormats(
|
||||
LedgerFormats::getInstance(),
|
||||
"LedgerFormats",
|
||||
ledgerObject.GetDescriptor(),
|
||||
ledgerObject.GetDescriptor()->FindOneofByName("object"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
testTransactionGRPCOneOfs()
|
||||
{
|
||||
testcase("Transaction validation");
|
||||
|
||||
org::xrpl::rpc::v1::Transaction const txData;
|
||||
|
||||
testKnownFormats(
|
||||
TxFormats::getInstance(),
|
||||
"TxFormats",
|
||||
txData.GetDescriptor(),
|
||||
txData.GetDescriptor()->FindOneofByName("transaction_data"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testLedgerObjectGRPCOneOfs();
|
||||
testTransactionGRPCOneOfs();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(KnownFormatToGRPC, protocol, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -491,227 +491,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// gRPC stuff
|
||||
class GetAccountInfoClient : public GRPCTestClientBase
|
||||
{
|
||||
public:
|
||||
org::xrpl::rpc::v1::GetAccountInfoRequest request;
|
||||
org::xrpl::rpc::v1::GetAccountInfoResponse reply;
|
||||
|
||||
explicit GetAccountInfoClient(std::string const& port)
|
||||
: GRPCTestClientBase(port)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GetAccountInfo()
|
||||
{
|
||||
status = stub_->GetAccountInfo(&context, request, &reply);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testSimpleGrpc()
|
||||
{
|
||||
testcase("gRPC simple");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
Account const alice{"alice"};
|
||||
env.fund(drops(1000 * 1000 * 1000), alice);
|
||||
|
||||
{
|
||||
// most simple case
|
||||
GetAccountInfoClient client(grpcPort);
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data().account().value().address() ==
|
||||
alice.human());
|
||||
}
|
||||
{
|
||||
GetAccountInfoClient client(grpcPort);
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.set_queue(true);
|
||||
client.request.mutable_ledger()->set_sequence(3);
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
return;
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data()
|
||||
.balance()
|
||||
.value()
|
||||
.xrp_amount()
|
||||
.drops() == 1000 * 1000 * 1000);
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data().account().value().address() ==
|
||||
alice.human());
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data().sequence().value() ==
|
||||
env.seq(alice));
|
||||
BEAST_EXPECT(client.reply.queue_data().txn_count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testErrorsGrpc()
|
||||
{
|
||||
testcase("gRPC errors");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
auto getClient = [&grpcPort]() {
|
||||
return GetAccountInfoClient(grpcPort);
|
||||
};
|
||||
Account const alice{"alice"};
|
||||
env.fund(drops(1000 * 1000 * 1000), alice);
|
||||
|
||||
{
|
||||
// bad address
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address("deadbeef");
|
||||
client.GetAccountInfo();
|
||||
BEAST_EXPECT(!client.status.ok());
|
||||
}
|
||||
{
|
||||
// no account
|
||||
Account const bogie{"bogie"};
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(bogie.human());
|
||||
client.GetAccountInfo();
|
||||
BEAST_EXPECT(!client.status.ok());
|
||||
}
|
||||
{
|
||||
// bad ledger_index
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.mutable_ledger()->set_sequence(0);
|
||||
client.GetAccountInfo();
|
||||
BEAST_EXPECT(!client.status.ok());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSignerListsGrpc()
|
||||
{
|
||||
testcase("gRPC singer lists");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
auto getClient = [&grpcPort]() {
|
||||
return GetAccountInfoClient(grpcPort);
|
||||
};
|
||||
|
||||
Account const alice{"alice"};
|
||||
env.fund(drops(1000 * 1000 * 1000), alice);
|
||||
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.set_signer_lists(true);
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
return;
|
||||
BEAST_EXPECT(client.reply.signer_list().signer_entries_size() == 0);
|
||||
}
|
||||
|
||||
// Give alice a SignerList.
|
||||
Account const bogie{"bogie"};
|
||||
Json::Value const smallSigners = signers(alice, 2, {{bogie, 3}});
|
||||
env(smallSigners);
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.set_signer_lists(false);
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
return;
|
||||
BEAST_EXPECT(client.reply.signer_list().signer_entries_size() == 0);
|
||||
}
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.set_signer_lists(true);
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data().owner_count().value() == 1);
|
||||
BEAST_EXPECT(client.reply.signer_list().signer_entries_size() == 1);
|
||||
}
|
||||
|
||||
// Give alice a big signer list
|
||||
Account const demon{"demon"};
|
||||
Account const ghost{"ghost"};
|
||||
Account const haunt{"haunt"};
|
||||
Account const jinni{"jinni"};
|
||||
Account const phase{"phase"};
|
||||
Account const shade{"shade"};
|
||||
Account const spook{"spook"};
|
||||
Json::Value const bigSigners = signers(
|
||||
alice,
|
||||
4,
|
||||
{
|
||||
{bogie, 1},
|
||||
{demon, 1},
|
||||
{ghost, 1},
|
||||
{haunt, 1},
|
||||
{jinni, 1},
|
||||
{phase, 1},
|
||||
{shade, 1},
|
||||
{spook, 1},
|
||||
});
|
||||
env(bigSigners);
|
||||
|
||||
std::set<std::string> accounts;
|
||||
accounts.insert(bogie.human());
|
||||
accounts.insert(demon.human());
|
||||
accounts.insert(ghost.human());
|
||||
accounts.insert(haunt.human());
|
||||
accounts.insert(jinni.human());
|
||||
accounts.insert(phase.human());
|
||||
accounts.insert(shade.human());
|
||||
accounts.insert(spook.human());
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.mutable_account()->set_address(alice.human());
|
||||
client.request.set_signer_lists(true);
|
||||
client.GetAccountInfo();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.account_data().owner_count().value() == 1);
|
||||
auto& signerList = client.reply.signer_list();
|
||||
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().value() == 1);
|
||||
BEAST_EXPECT(
|
||||
accounts.erase(signerList.signer_entries(i)
|
||||
.account()
|
||||
.value()
|
||||
.address()) == 1);
|
||||
}
|
||||
BEAST_EXPECT(accounts.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -719,9 +498,6 @@ public:
|
||||
testSignerLists();
|
||||
testSignerListsApiVersion2();
|
||||
testSignerListsV2();
|
||||
testSimpleGrpc();
|
||||
testErrorsGrpc();
|
||||
testSignerListsGrpc();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/basics/mulDiv.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
#include <test/rpc/GRPCTestClientBase.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Fee_test : public beast::unit_test::suite
|
||||
{
|
||||
class GrpcFeeClient : public GRPCTestClientBase
|
||||
{
|
||||
public:
|
||||
org::xrpl::rpc::v1::GetFeeRequest request;
|
||||
org::xrpl::rpc::v1::GetFeeResponse reply;
|
||||
|
||||
explicit GrpcFeeClient(std::string const& grpcPort)
|
||||
: GRPCTestClientBase(grpcPort)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GetFee()
|
||||
{
|
||||
status = stub_->GetFee(&context, request, &reply);
|
||||
}
|
||||
};
|
||||
|
||||
std::pair<bool, org::xrpl::rpc::v1::GetFeeResponse>
|
||||
grpcGetFee(std::string const& grpcPort)
|
||||
{
|
||||
GrpcFeeClient client(grpcPort);
|
||||
client.GetFee();
|
||||
return std::pair<bool, org::xrpl::rpc::v1::GetFeeResponse>(
|
||||
client.status.ok(), client.reply);
|
||||
}
|
||||
|
||||
void
|
||||
testFeeGrpc()
|
||||
{
|
||||
testcase("Test Fee Grpc");
|
||||
|
||||
using namespace test::jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
Account A1{"A1"};
|
||||
Account A2{"A2"};
|
||||
env.fund(XRP(10000), A1);
|
||||
env.fund(XRP(10000), A2);
|
||||
env.close();
|
||||
env.trust(A2["USD"](1000), A1);
|
||||
env.close();
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
env(pay(A2, A1, A2["USD"](100)));
|
||||
if (i == 4)
|
||||
env.close();
|
||||
}
|
||||
|
||||
auto view = env.current();
|
||||
|
||||
auto const metrics = env.app().getTxQ().getMetrics(*env.current());
|
||||
|
||||
auto const result = grpcGetFee(grpcPort);
|
||||
|
||||
BEAST_EXPECT(result.first == true);
|
||||
|
||||
auto reply = result.second;
|
||||
|
||||
// current ledger data
|
||||
BEAST_EXPECT(reply.current_ledger_size() == metrics.txInLedger);
|
||||
BEAST_EXPECT(reply.current_queue_size() == metrics.txCount);
|
||||
BEAST_EXPECT(reply.expected_ledger_size() == metrics.txPerLedger);
|
||||
BEAST_EXPECT(reply.ledger_current_index() == view->info().seq);
|
||||
BEAST_EXPECT(reply.max_queue_size() == *metrics.txQMaxSize);
|
||||
|
||||
// fee levels data
|
||||
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
|
||||
org::xrpl::rpc::v1::Fee& fee = *reply.mutable_fee();
|
||||
auto const baseFee = view->fees().base;
|
||||
BEAST_EXPECT(
|
||||
fee.base_fee().drops() ==
|
||||
toDrops(metrics.referenceFeeLevel, baseFee));
|
||||
BEAST_EXPECT(
|
||||
fee.minimum_fee().drops() ==
|
||||
toDrops(metrics.minProcessingFeeLevel, baseFee));
|
||||
BEAST_EXPECT(
|
||||
fee.median_fee().drops() == toDrops(metrics.medFeeLevel, baseFee));
|
||||
auto openLedgerFee =
|
||||
toDrops(metrics.openLedgerFeeLevel - FeeLevel64{1}, baseFee) + 1;
|
||||
BEAST_EXPECT(fee.open_ledger_fee().drops() == openLedgerFee.drops());
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testFeeGrpc();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Fee, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -773,25 +773,6 @@ class ReportingETL_test : public beast::unit_test::suite
|
||||
testNeedCurrentOrClosed()
|
||||
{
|
||||
testcase("NeedCurrentOrClosed");
|
||||
{
|
||||
org::xrpl::rpc::v1::GetAccountInfoRequest request;
|
||||
request.mutable_ledger()->set_sequence(1);
|
||||
BEAST_EXPECT(!needCurrentOrClosed(request));
|
||||
request.mutable_ledger()->set_hash("");
|
||||
BEAST_EXPECT(!needCurrentOrClosed(request));
|
||||
request.mutable_ledger()->set_shortcut(
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED);
|
||||
BEAST_EXPECT(!needCurrentOrClosed(request));
|
||||
request.mutable_ledger()->set_shortcut(
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_UNSPECIFIED);
|
||||
BEAST_EXPECT(!needCurrentOrClosed(request));
|
||||
request.mutable_ledger()->set_shortcut(
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT);
|
||||
BEAST_EXPECT(needCurrentOrClosed(request));
|
||||
request.mutable_ledger()->set_shortcut(
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED);
|
||||
BEAST_EXPECT(needCurrentOrClosed(request));
|
||||
}
|
||||
|
||||
{
|
||||
org::xrpl::rpc::v1::GetLedgerRequest request;
|
||||
@@ -904,18 +885,6 @@ class ReportingETL_test : public beast::unit_test::suite
|
||||
org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT);
|
||||
BEAST_EXPECT(needCurrentOrClosed(request));
|
||||
}
|
||||
|
||||
{
|
||||
org::xrpl::rpc::v1::GetFeeRequest feeRequest;
|
||||
BEAST_EXPECT(!needCurrentOrClosed(feeRequest));
|
||||
|
||||
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest
|
||||
accountTxRequest;
|
||||
BEAST_EXPECT(!needCurrentOrClosed(accountTxRequest));
|
||||
|
||||
org::xrpl::rpc::v1::GetTransactionRequest txRequest;
|
||||
BEAST_EXPECT(!needCurrentOrClosed(txRequest));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/WSClient.h>
|
||||
|
||||
#include <ripple/resource/Charge.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
#include <test/rpc/GRPCTestClientBase.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Submit_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
class SubmitClient : public GRPCTestClientBase
|
||||
{
|
||||
public:
|
||||
org::xrpl::rpc::v1::SubmitTransactionRequest request;
|
||||
org::xrpl::rpc::v1::SubmitTransactionResponse reply;
|
||||
|
||||
explicit SubmitClient(std::string const& port)
|
||||
: GRPCTestClientBase(port)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SubmitTransaction()
|
||||
{
|
||||
status = stub_->SubmitTransaction(&context, request, &reply);
|
||||
}
|
||||
};
|
||||
|
||||
struct TestData
|
||||
{
|
||||
std::string xrpTxBlob;
|
||||
std::string xrpTxHash;
|
||||
std::string usdTxBlob;
|
||||
std::string usdTxHash;
|
||||
const static int fund = 10000;
|
||||
} testData;
|
||||
|
||||
void
|
||||
fillTestData()
|
||||
{
|
||||
testcase("fill test data");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this, envconfig(addGrpcConfig));
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(TestData::fund), "alice", "bob");
|
||||
env.trust(bob["USD"](TestData::fund), alice);
|
||||
env.close();
|
||||
|
||||
auto toBinary = [this](std::string const& text) {
|
||||
auto blob = strUnHex(text);
|
||||
BEAST_EXPECT(blob);
|
||||
return std::string{
|
||||
reinterpret_cast<char const*>(blob->data()), blob->size()};
|
||||
};
|
||||
|
||||
// use a websocket client to fill transaction blobs
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
{
|
||||
Json::Value jrequestXrp;
|
||||
jrequestXrp[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jrequestXrp[jss::tx_json] =
|
||||
pay("alice", "bob", XRP(TestData::fund / 2));
|
||||
Json::Value jreply_xrp = wsc->invoke("sign", jrequestXrp);
|
||||
|
||||
if (!BEAST_EXPECT(jreply_xrp.isMember(jss::result)))
|
||||
return;
|
||||
if (!BEAST_EXPECT(jreply_xrp[jss::result].isMember(jss::tx_blob)))
|
||||
return;
|
||||
testData.xrpTxBlob =
|
||||
toBinary(jreply_xrp[jss::result][jss::tx_blob].asString());
|
||||
if (!BEAST_EXPECT(jreply_xrp[jss::result].isMember(jss::tx_json)))
|
||||
return;
|
||||
if (!BEAST_EXPECT(
|
||||
jreply_xrp[jss::result][jss::tx_json].isMember(jss::hash)))
|
||||
return;
|
||||
testData.xrpTxHash = toBinary(
|
||||
jreply_xrp[jss::result][jss::tx_json][jss::hash].asString());
|
||||
}
|
||||
{
|
||||
Json::Value jrequestUsd;
|
||||
jrequestUsd[jss::secret] = toBase58(generateSeed("bob"));
|
||||
jrequestUsd[jss::tx_json] =
|
||||
pay("bob", "alice", bob["USD"](TestData::fund / 2));
|
||||
Json::Value jreply_usd = wsc->invoke("sign", jrequestUsd);
|
||||
|
||||
if (!BEAST_EXPECT(jreply_usd.isMember(jss::result)))
|
||||
return;
|
||||
if (!BEAST_EXPECT(jreply_usd[jss::result].isMember(jss::tx_blob)))
|
||||
return;
|
||||
testData.usdTxBlob =
|
||||
toBinary(jreply_usd[jss::result][jss::tx_blob].asString());
|
||||
if (!BEAST_EXPECT(jreply_usd[jss::result].isMember(jss::tx_json)))
|
||||
return;
|
||||
if (!BEAST_EXPECT(
|
||||
jreply_usd[jss::result][jss::tx_json].isMember(jss::hash)))
|
||||
return;
|
||||
testData.usdTxHash = toBinary(
|
||||
jreply_usd[jss::result][jss::tx_json][jss::hash].asString());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSubmitGoodBlobGrpc()
|
||||
{
|
||||
testcase("Submit good blobs, XRP, USD, and same transaction twice");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(TestData::fund), "alice", "bob");
|
||||
env.trust(bob["USD"](TestData::fund), alice);
|
||||
env.close();
|
||||
|
||||
auto getClient = [&grpcPort]() { return SubmitClient(grpcPort); };
|
||||
|
||||
// XRP
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.set_signed_transaction(testData.xrpTxBlob);
|
||||
client.SubmitTransaction();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(client.reply.engine_result().result() == "tesSUCCESS");
|
||||
BEAST_EXPECT(client.reply.engine_result_code() == 0);
|
||||
BEAST_EXPECT(client.reply.hash() == testData.xrpTxHash);
|
||||
}
|
||||
// USD
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.set_signed_transaction(testData.usdTxBlob);
|
||||
client.SubmitTransaction();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(client.reply.engine_result().result() == "tesSUCCESS");
|
||||
BEAST_EXPECT(client.reply.engine_result_code() == 0);
|
||||
BEAST_EXPECT(client.reply.hash() == testData.usdTxHash);
|
||||
}
|
||||
// USD, error, same transaction again
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.set_signed_transaction(testData.usdTxBlob);
|
||||
client.SubmitTransaction();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.engine_result().result() == "tefPAST_SEQ");
|
||||
BEAST_EXPECT(client.reply.engine_result_code() == -190);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSubmitErrorBlobGrpc()
|
||||
{
|
||||
testcase("Submit error, bad blob, no account");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
|
||||
auto getClient = [&grpcPort]() { return SubmitClient(grpcPort); };
|
||||
|
||||
// short transaction blob, cannot parse
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.set_signed_transaction("deadbeef");
|
||||
client.SubmitTransaction();
|
||||
BEAST_EXPECT(!client.status.ok());
|
||||
}
|
||||
// bad blob with correct length, cannot parse
|
||||
{
|
||||
auto client = getClient();
|
||||
auto xrpTxBlobCopy(testData.xrpTxBlob);
|
||||
std::reverse(xrpTxBlobCopy.begin(), xrpTxBlobCopy.end());
|
||||
client.request.set_signed_transaction(xrpTxBlobCopy);
|
||||
client.SubmitTransaction();
|
||||
BEAST_EXPECT(!client.status.ok());
|
||||
}
|
||||
// good blob, can parse but no account
|
||||
{
|
||||
auto client = getClient();
|
||||
client.request.set_signed_transaction(testData.xrpTxBlob);
|
||||
client.SubmitTransaction();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.engine_result().result() == "terNO_ACCOUNT");
|
||||
BEAST_EXPECT(client.reply.engine_result_code() == -96);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testSubmitInsufficientFundsGrpc()
|
||||
{
|
||||
testcase("Submit good blobs but insufficient funds");
|
||||
|
||||
using namespace jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
// fund 1000 (TestData::fund/10) XRP, the transaction sends 5000
|
||||
// (TestData::fund/2) XRP, so insufficient funds
|
||||
env.fund(XRP(TestData::fund / 10), "alice", "bob");
|
||||
env.trust(bob["USD"](TestData::fund), alice);
|
||||
env.close();
|
||||
|
||||
{
|
||||
SubmitClient client(grpcPort);
|
||||
client.request.set_signed_transaction(testData.xrpTxBlob);
|
||||
client.SubmitTransaction();
|
||||
if (!BEAST_EXPECT(client.status.ok()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
client.reply.engine_result().result() == "tecUNFUNDED_PAYMENT");
|
||||
BEAST_EXPECT(client.reply.engine_result_code() == 104);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
fillTestData();
|
||||
testSubmitGoodBlobGrpc();
|
||||
testSubmitErrorBlobGrpc();
|
||||
testSubmitInsufficientFundsGrpc();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Submit, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,829 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/rdb/backend/SQLiteDatabase.h>
|
||||
#include <ripple/basics/mulDiv.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#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 {
|
||||
|
||||
class Tx_test : public beast::unit_test::suite
|
||||
{
|
||||
template <class T>
|
||||
std::string
|
||||
toByteString(T const& data)
|
||||
{
|
||||
const char* bytes = reinterpret_cast<const char*>(data.data());
|
||||
return {bytes, data.size()};
|
||||
}
|
||||
|
||||
void
|
||||
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
|
||||
{
|
||||
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;
|
||||
BEAST_EXPECT(
|
||||
issuedCurrency.currency().name() == to_string(currency));
|
||||
BEAST_EXPECT(
|
||||
issuedCurrency.currency().code() == toByteString(currency));
|
||||
BEAST_EXPECT(issuedCurrency.value() == to_string(amount.iou()));
|
||||
BEAST_EXPECT(
|
||||
issuedCurrency.issuer().address() == toBase58(issue.account));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
|
||||
if (!BEAST_EXPECT(proto.has_account()))
|
||||
return;
|
||||
BEAST_EXPECT(proto.account().value().address() == toBase58(account));
|
||||
|
||||
STAmount amount = txnSt->getFieldAmount(sfAmount);
|
||||
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().value().address() ==
|
||||
toBase58(accountDest));
|
||||
|
||||
STAmount fee = txnSt->getFieldAmount(sfFee);
|
||||
if (!BEAST_EXPECT(proto.has_fee()))
|
||||
return;
|
||||
BEAST_EXPECT(proto.fee().drops() == fee.xrp().drops());
|
||||
|
||||
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().value() == toByteString(signingPubKey));
|
||||
|
||||
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().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().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());
|
||||
}
|
||||
|
||||
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 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 org::xrpl::rpc::v1::Payment_PathElement& protoElement =
|
||||
protoPath.elements(ind2++);
|
||||
STPathElement const& elt = *it2;
|
||||
|
||||
if (elt.isOffer())
|
||||
{
|
||||
if (elt.hasCurrency())
|
||||
{
|
||||
Currency const& currency = elt.getCurrency();
|
||||
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();
|
||||
if (BEAST_EXPECT(protoElement.has_issuer()))
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
protoElement.issuer().address() ==
|
||||
toBase58(issuer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!protoElement.has_issuer());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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 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()));
|
||||
|
||||
org::xrpl::rpc::v1::TransactionResult r;
|
||||
|
||||
RPC::convert(r, txMeta->getResultTER());
|
||||
|
||||
BEAST_EXPECT(
|
||||
proto.transaction_result().result_type() == r.result_type());
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gRPC stuff
|
||||
class GrpcTxClient : public GRPCTestClientBase
|
||||
{
|
||||
public:
|
||||
org::xrpl::rpc::v1::GetTransactionRequest request;
|
||||
org::xrpl::rpc::v1::GetTransactionResponse reply;
|
||||
|
||||
explicit GrpcTxClient(std::string const& port)
|
||||
: GRPCTestClientBase(port)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Tx()
|
||||
{
|
||||
status = stub_->GetTransaction(&context, request, &reply);
|
||||
}
|
||||
};
|
||||
|
||||
class GrpcAccountTxClient : public GRPCTestClientBase
|
||||
{
|
||||
public:
|
||||
org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest request;
|
||||
org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse reply;
|
||||
|
||||
explicit GrpcAccountTxClient(std::string const& port)
|
||||
: GRPCTestClientBase(port)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AccountTx()
|
||||
{
|
||||
status =
|
||||
stub_->GetAccountTransactionHistory(&context, request, &reply);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testTxGrpc()
|
||||
{
|
||||
testcase("Test Tx Grpc");
|
||||
|
||||
using namespace test::jtx;
|
||||
std::unique_ptr<Config> config = envconfig(addGrpcConfig);
|
||||
std::string grpcPort = *(*config)["port_grpc"].get<std::string>("port");
|
||||
Env env(*this, std::move(config));
|
||||
|
||||
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, 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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
auto& ledgerMaster = env.app().getLedgerMaster();
|
||||
int index = startLegSeq;
|
||||
for (auto&& tx : txns)
|
||||
{
|
||||
auto id = tx->getTransactionID();
|
||||
auto ledger = ledgerMaster.getLedgerBySeq(index);
|
||||
|
||||
for (bool b : {false, true})
|
||||
{
|
||||
auto const result = grpcTx(id, b);
|
||||
|
||||
BEAST_EXPECT(result.first == true);
|
||||
BEAST_EXPECT(result.second.ledger_index() == index);
|
||||
BEAST_EXPECT(result.second.validated() == true);
|
||||
if (b)
|
||||
{
|
||||
Serializer s = tx->getSerializer();
|
||||
BEAST_EXPECT(
|
||||
result.second.transaction_binary() == toByteString(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpPaymentTx(result.second.transaction(), tx);
|
||||
}
|
||||
|
||||
if (!ledger || b)
|
||||
continue;
|
||||
|
||||
auto rawMeta = ledger->txRead(id).second;
|
||||
if (!rawMeta)
|
||||
continue;
|
||||
|
||||
auto txMeta =
|
||||
std::make_shared<TxMeta>(id, ledger->seq(), *rawMeta);
|
||||
|
||||
cmpMeta(result.second.meta(), txMeta);
|
||||
cmpDeliveredAmount(
|
||||
result.second.meta(),
|
||||
result.second.transaction(),
|
||||
txMeta,
|
||||
tx);
|
||||
|
||||
auto grpcAccountTx = [&grpcPort](
|
||||
uint256 const& id,
|
||||
bool binary,
|
||||
AccountID const& account)
|
||||
-> std::
|
||||
pair<bool, org::xrpl::rpc::v1::GetTransactionResponse> {
|
||||
GrpcAccountTxClient client(grpcPort);
|
||||
client.request.set_binary(binary);
|
||||
client.request.mutable_account()->set_address(
|
||||
toBase58(account));
|
||||
client.AccountTx();
|
||||
org::xrpl::rpc::v1::GetTransactionResponse res;
|
||||
|
||||
for (auto const& tx : client.reply.transactions())
|
||||
{
|
||||
if (uint256::fromVoid(tx.hash().data()) == id)
|
||||
{
|
||||
return {client.status.ok(), tx};
|
||||
}
|
||||
}
|
||||
return {false, res};
|
||||
};
|
||||
|
||||
// Compare result to result from account_tx
|
||||
auto mentioned = tx->getMentionedAccounts();
|
||||
|
||||
if (!BEAST_EXPECT(mentioned.size()))
|
||||
continue;
|
||||
|
||||
auto account = *mentioned.begin();
|
||||
auto const accountTxResult = grpcAccountTx(id, b, account);
|
||||
|
||||
if (!BEAST_EXPECT(accountTxResult.first))
|
||||
continue;
|
||||
|
||||
cmpPaymentTx(accountTxResult.second.transaction(), tx);
|
||||
cmpMeta(accountTxResult.second.meta(), txMeta);
|
||||
cmpDeliveredAmount(
|
||||
accountTxResult.second.meta(),
|
||||
accountTxResult.second.transaction(),
|
||||
txMeta,
|
||||
tx);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// Find not existing transaction
|
||||
auto const tx = env.jt(noop(A1), seq(env.seq(A1))).stx;
|
||||
for (bool b : {false, true})
|
||||
{
|
||||
auto const result = grpcTx(tx->getTransactionID(), b);
|
||||
|
||||
BEAST_EXPECT(result.first == false);
|
||||
}
|
||||
|
||||
// Delete one transaction
|
||||
const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
|
||||
{
|
||||
// Remove one of the ledgers from the database directly
|
||||
dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
|
||||
->deleteTransactionByLedgerSeq(deletedLedger);
|
||||
}
|
||||
|
||||
for (bool b : {false, true})
|
||||
{
|
||||
auto const result = grpcTx(tx->getTransactionID(), b);
|
||||
|
||||
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:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testTxGrpc();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Tx, app, ripple);
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user