From eed1a891a7ec36c18e263b42eff5e4666ae1a8da Mon Sep 17 00:00:00 2001 From: Scott Schurr Date: Wed, 2 Sep 2015 16:58:49 -0700 Subject: [PATCH] Remove TxnSignApiFacade (RIPD-945): Replace TxnSignApiFacade with separate passed in arguments to the various sign/submit RPC commands. Also increase unit test coverage of the submit_multisign RPC command. --- src/ripple/rpc/handlers/SignFor.cpp | 9 +- src/ripple/rpc/handlers/SignHandler.cpp | 12 +- src/ripple/rpc/handlers/Submit.cpp | 15 +- src/ripple/rpc/handlers/SubmitMultiSigned.cpp | 10 +- src/ripple/rpc/impl/TransactionSign.cpp | 449 ++++-------- src/ripple/rpc/impl/TransactionSign.h | 192 ++--- src/ripple/rpc/tests/JSONRPC.test.cpp | 681 ++++++++++++++++-- 7 files changed, 872 insertions(+), 496 deletions(-) diff --git a/src/ripple/rpc/handlers/SignFor.cpp b/src/ripple/rpc/handlers/SignFor.cpp index 934915d4a..68cf9ae6d 100755 --- a/src/ripple/rpc/handlers/SignFor.cpp +++ b/src/ripple/rpc/handlers/SignFor.cpp @@ -18,8 +18,8 @@ //============================================================================== #include -#include #include +#include #include #include #include @@ -46,7 +46,12 @@ Json::Value doSignFor (RPC::Context& context) auto const failType = NetworkOPs::doFailHard (failHard); return RPC::transactionSignFor ( - context.params, failType, context.netOps, context.role); + context.params, + failType, + context.role, + context.ledgerMaster.getValidatedLedgerAge(), + context.app.getFeeTrack(), + context.ledgerMaster.getCurrentLedger()); } } // ripple diff --git a/src/ripple/rpc/handlers/SignHandler.cpp b/src/ripple/rpc/handlers/SignHandler.cpp index aebe5fcac..dfe637562 100644 --- a/src/ripple/rpc/handlers/SignHandler.cpp +++ b/src/ripple/rpc/handlers/SignHandler.cpp @@ -18,11 +18,8 @@ //============================================================================== #include -#include -#include -#include +#include #include -#include #include #include #include @@ -42,7 +39,12 @@ Json::Value doSign (RPC::Context& context) && context.params[jss::fail_hard].asBool ()); return RPC::transactionSign ( - context.params, failType, context.netOps, context.role); + context.params, + failType, + context.role, + context.ledgerMaster.getValidatedLedgerAge(), + context.app.getFeeTrack(), + context.ledgerMaster.getCurrentLedger()); } } // ripple diff --git a/src/ripple/rpc/handlers/Submit.cpp b/src/ripple/rpc/handlers/Submit.cpp index 23d7cb795..556aeea0e 100644 --- a/src/ripple/rpc/handlers/Submit.cpp +++ b/src/ripple/rpc/handlers/Submit.cpp @@ -18,18 +18,13 @@ //============================================================================== #include -#include -#include +#include #include -#include -#include #include #include #include -#include #include #include -#include namespace ripple { @@ -53,7 +48,13 @@ Json::Value doSubmit (RPC::Context& context) auto const failType = getFailHard (context); return RPC::transactionSubmit ( - context.params, failType, context.netOps, context.role); + context.params, + failType, + context.role, + context.ledgerMaster.getValidatedLedgerAge(), + context.app.getFeeTrack(), + context.ledgerMaster.getCurrentLedger(), + RPC::getProcessTxnFn (context.netOps)); } Json::Value jvResult; diff --git a/src/ripple/rpc/handlers/SubmitMultiSigned.cpp b/src/ripple/rpc/handlers/SubmitMultiSigned.cpp index 405bf68f9..7e45b7c47 100644 --- a/src/ripple/rpc/handlers/SubmitMultiSigned.cpp +++ b/src/ripple/rpc/handlers/SubmitMultiSigned.cpp @@ -18,8 +18,8 @@ //============================================================================== #include -#include #include +#include #include #include #include @@ -45,7 +45,13 @@ Json::Value doSubmitMultiSigned (RPC::Context& context) auto const failType = NetworkOPs::doFailHard (failHard); return RPC::transactionSubmitMultiSigned ( - context.params, failType, context.netOps, context.role); + context.params, + failType, + context.role, + context.ledgerMaster.getValidatedLedgerAge(), + context.app.getFeeTrack(), + context.ledgerMaster.getCurrentLedger(), + RPC::getProcessTxnFn (context.netOps)); } } // ripple diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index f8c4a890f..c7dea5b7e 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -18,23 +18,19 @@ //============================================================================== #include +#include #include -#include #include #include -#include #include -#include #include #include #include #include #include #include -#include #include #include -#include #include namespace ripple { @@ -99,109 +95,7 @@ public: //------------------------------------------------------------------------------ -// TxnSignApiFacade methods - -void TxnSignApiFacade::snapshotAccountState (AccountID const& accountID) -{ - if (!netOPs_) // Unit testing. - return; - - ledger_ = getApp().getLedgerMaster ().getCurrentLedger (); - accountID_ = accountID; - sle_ = cachedRead(*ledger_, - keylet::account(accountID_).key, ltACCOUNT_ROOT); -} - -bool TxnSignApiFacade::isValidAccount () const -{ - if (!ledger_) // Unit testing. - return true; - - return static_cast (sle_); -} - -std::uint32_t TxnSignApiFacade::getSeq () const -{ - if (!ledger_) // Unit testing. - return 0; - - return sle_->getFieldU32(sfSequence); -} - -void TxnSignApiFacade::processTransaction ( - Transaction::pointer& transaction, - bool bAdmin, - bool bLocal, - NetworkOPs::FailHard failType) -{ - if (!netOPs_) // Unit testing. - return; - - netOPs_->processTransaction(transaction, bAdmin, bLocal, failType); -} - -boost::optional -TxnSignApiFacade::findPathsForOneIssuer ( - AccountID const& dstAccountID, - Issue const& srcIssue, - STAmount const& dstAmount, - int searchLevel, - unsigned int const maxPaths, - STPathSet const& paths, - STPath& fullLiquidityPath) const -{ - if (! ledger_) // Unit testing. - { - // Note that unit tests don't (yet) need paths or fullLiquidityPath. - boost::optional result; - result.emplace(); - return result; - } - - auto cache = std::make_shared (ledger_); - return ripple::findPathsForOneIssuer( - cache, - accountID_, - dstAccountID, - srcIssue, - dstAmount, - searchLevel, - maxPaths, - paths, - fullLiquidityPath); -} - -std::uint64_t TxnSignApiFacade::scaleFeeBase (std::uint64_t fee) const -{ - if (!ledger_) // Unit testing. - return fee; - - // VFALCO Audit - return getApp().getFeeTrack().scaleFeeBase( - fee, ledger_->fees().base, ledger_->fees().units); -} - -std::uint64_t -TxnSignApiFacade::scaleFeeLoad (std::uint64_t fee, bool bAdmin) const -{ - if (!ledger_) // Unit testing. - return fee; - - // VFALCO Audit - return getApp().getFeeTrack().scaleFeeLoad( - fee, ledger_->fees().base, ledger_->fees().units, - bAdmin); -} - -bool TxnSignApiFacade::hasAccountRoot () const -{ - if (!netOPs_) // Unit testing. - return true; - return ledger_->exists( - keylet::account(accountID_)); -} - -error_code_i acctMatchesPubKey ( +static error_code_i acctMatchesPubKey ( std::shared_ptr accountState, AccountID const& accountID, RippleAddress const& publicKey) @@ -236,134 +130,13 @@ error_code_i acctMatchesPubKey ( return rpcBAD_SECRET; } -error_code_i TxnSignApiFacade::singleAcctMatchesPubKey ( - RippleAddress const& publicKey) const -{ - if (!netOPs_) // Unit testing. - return rpcSUCCESS; - - return acctMatchesPubKey (sle_, accountID_, publicKey); -} - -error_code_i TxnSignApiFacade::multiAcctMatchesPubKey ( - AccountID const& accountID, - RippleAddress const& publicKey) const -{ - std::shared_ptr accountState; - // VFALCO Do we need to check netOPs_? - if (netOPs_ && ledger_) - { - // If it's available, get the account root for the multi-signer's - // accountID. It's okay if the account root is not available, - // since they might be signing with a phantom (unfunded) account. - accountState = cachedRead(*ledger_, - keylet::account(accountID).key, ltACCOUNT_ROOT); - } - - return acctMatchesPubKey (accountState, accountID, publicKey); -} - -int TxnSignApiFacade::getValidatedLedgerAge () const -{ - if (!netOPs_) // Unit testing. - return 0; - - return getApp().getLedgerMaster ().getValidatedLedgerAge (); -} - -bool TxnSignApiFacade::isLoadedCluster () const -{ - if (!netOPs_) // Unit testing. - return false; - - return getApp().getFeeTrack().isLoadedCluster(); -} - -//------------------------------------------------------------------------------ - -/** Fill in the fee on behalf of the client. - This is called when the client does not explicitly specify the fee. - The client may also put a ceiling on the amount of the fee. This ceiling - is expressed as a multiplier based on the current ledger's fee schedule. - - JSON fields - - "Fee" The fee paid by the transaction. Omitted when the client - wants the fee filled in. - - "fee_mult_max" A multiplier applied to the current ledger's transaction - fee that caps the maximum the fee server should auto fill. - If this optional field is not specified, then a default - multiplier is used. - - @param tx The JSON corresponding to the transaction to fill in. - @param ledger A ledger for retrieving the current fee schedule. - @param roll Identifies if this is called by an administrative endpoint. - - @return A JSON object containing the error results, if any -*/ - -Json::Value checkFee ( - Json::Value& request, - TxnSignApiFacade& apiFacade, - Role const role, - AutoFill const doAutoFill) -{ - Json::Value& tx (request[jss::tx_json]); - if (tx.isMember (jss::Fee)) - return Json::Value(); - - if (doAutoFill == AutoFill::dont) - return RPC::missing_field_error ("tx_json.Fee"); - - int mult = Tuning::defaultAutoFillFeeMultiplier; - if (request.isMember (jss::fee_mult_max)) - { - if (request[jss::fee_mult_max].isNumeric ()) - { - mult = request[jss::fee_mult_max].asInt(); - } - else - { - return RPC::make_error (rpcHIGH_FEE, - RPC::expected_field_message (jss::fee_mult_max, "a number")); - } - } - - // Default fee in fee units. - std::uint64_t const feeDefault = getConfig().TRANSACTION_FEE_BASE; - - // Administrative endpoints are exempt from local fees. - std::uint64_t const fee = - apiFacade.scaleFeeLoad (feeDefault, role == Role::ADMIN); - - std::uint64_t const limit = mult * apiFacade.scaleFeeBase (feeDefault); - - if (fee > limit) - { - std::stringstream ss; - ss << "Fee of " << fee - << " exceeds the requested tx limit of " << limit; - return RPC::make_error (rpcHIGH_FEE, ss.str()); - } - - tx [jss::Fee] = static_cast(fee); - return Json::Value(); -} - -enum class PathFind : unsigned char -{ - dont, - might -}; - static Json::Value checkPayment( Json::Value const& params, Json::Value& tx_json, AccountID const& srcAddressID, - TxnSignApiFacade const& apiFacade, Role const role, - PathFind const doPath) + std::shared_ptr& ledger, + bool doPath) { // Only path find for Payments. if (tx_json[jss::TransactionType].asString () != "Payment") @@ -385,7 +158,7 @@ static Json::Value checkPayment( if (! dstAccountID) return RPC::invalid_field_error ("tx_json.Destination"); - if ((doPath == PathFind::dont) && params.isMember (jss::build_path)) + if ((doPath == false) && params.isMember (jss::build_path)) return RPC::make_error (rpcINVALID_PARAMS, "Field 'build_path' not allowed in this context."); @@ -419,11 +192,14 @@ static Json::Value checkPayment( return rpcError (rpcTOO_BUSY); STPath fullLiquidityPath; - auto result = apiFacade.findPathsForOneIssuer ( + auto cache = std::make_shared (ledger); + auto result = findPathsForOneIssuer ( + cache, + srcAddressID, *dstAccountID, - sendMax.issue (), + sendMax.issue(), amount, - getConfig ().PATH_SEARCH_OLD, + getConfig().PATH_SEARCH_OLD, 4, // iMaxPaths {}, fullLiquidityPath); @@ -458,9 +234,10 @@ static Json::Value checkPayment( static std::pair checkTxJsonFields ( Json::Value const& tx_json, - TxnSignApiFacade const& apiFacade, Role const role, - bool const verify) + bool const verify, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack) { std::pair ret; @@ -495,15 +272,14 @@ checkTxJsonFields ( // Check for current ledger. if (verify && !getConfig ().RUN_STANDALONE && - (apiFacade.getValidatedLedgerAge() > - Tuning::maxValidatedLedgerAge)) + (validatedLedgerAge > Tuning::maxValidatedLedgerAge)) { ret.first = rpcError (rpcNO_CURRENT); return ret; } // Check for load. - if (apiFacade.isLoadedCluster() && (role != Role::ADMIN)) + if (feeTrack.isLoadedCluster() && (role != Role::ADMIN)) { ret.first = rpcError (rpcTOO_BUSY); return ret; @@ -550,9 +326,11 @@ static transactionPreProcessResult transactionPreProcessImpl ( Json::Value& params, - TxnSignApiFacade& apiFacade, Role role, - SigningForParams& signingArgs) + SigningForParams& signingArgs, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger) { KeyPair keypair; { @@ -571,7 +349,8 @@ transactionPreProcessImpl ( Json::Value& tx_json (params [jss::tx_json]); // Check tx_json fields, but don't add any. - auto txJsonResult = checkTxJsonFields (tx_json, apiFacade, role, verify); + auto txJsonResult = + checkTxJsonFields (tx_json, role, verify, validatedLedgerAge, feeTrack); if (RPC::contains_error (txJsonResult.first)) return std::move (txJsonResult.first); @@ -583,27 +362,27 @@ transactionPreProcessImpl ( if (!verify && !tx_json.isMember (jss::Sequence)) return RPC::missing_field_error ("tx_json.Sequence"); - apiFacade.snapshotAccountState (srcAddressID); + std::shared_ptr sle = cachedRead(*ledger, + keylet::account(srcAddressID).key, ltACCOUNT_ROOT); - if (verify) { - if (!apiFacade.isValidAccount()) - { - // If not offline and did not find account, error. - WriteLog (lsDEBUG, RPCHandler) - << "transactionSign: Failed to find source account " - << "in current ledger: " - << toBase58(srcAddressID); + if (verify && !sle) + { + // If not offline and did not find account, error. + WriteLog (lsDEBUG, RPCHandler) + << "transactionSign: Failed to find source account " + << "in current ledger: " + << toBase58(srcAddressID); - return rpcError (rpcSRC_ACT_NOT_FOUND); - } + return rpcError (rpcSRC_ACT_NOT_FOUND); } { Json::Value err = checkFee ( params, - apiFacade, role, - signingArgs.editFields() ? AutoFill::might : AutoFill::dont); + signingArgs.editFields(), + feeTrack, + ledger); if (RPC::contains_error (err)) return std::move (err); @@ -612,9 +391,9 @@ transactionPreProcessImpl ( params, tx_json, srcAddressID, - apiFacade, role, - signingArgs.editFields() ? PathFind::might : PathFind::dont); + ledger, + signingArgs.editFields()); if (RPC::contains_error(err)) return std::move (err); @@ -623,7 +402,18 @@ transactionPreProcessImpl ( if (signingArgs.editFields()) { if (!tx_json.isMember (jss::Sequence)) - tx_json[jss::Sequence] = apiFacade.getSeq(); + { + if (! sle) + { + WriteLog (lsDEBUG, RPCHandler) + << "transactionSign: Failed to find source account " + << "in current ledger: " + << toBase58(srcAddressID); + + return rpcError (rpcSRC_ACT_NOT_FOUND); + } + tx_json[jss::Sequence] = (*sle)[sfSequence]; + } if (!tx_json.isMember (jss::Flags)) tx_json[jss::Flags] = tfFullyCanonicalSig; @@ -631,7 +421,7 @@ transactionPreProcessImpl ( if (verify) { - if (!apiFacade.hasAccountRoot()) + if (! sle) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); @@ -648,7 +438,7 @@ transactionPreProcessImpl ( { // Make sure the account and secret belong together. error_code_i const err = - apiFacade.singleAcctMatchesPubKey (keypair.publicKey); + acctMatchesPubKey (sle, srcAddressID, keypair.publicKey); if (err != rpcSUCCESS) return rpcError (err); @@ -763,7 +553,7 @@ transactionConstructImpl (STTx::pointer stpTrans) return ret; } -Json::Value transactionFormatResultImpl (Transaction::pointer tpTrans) +static Json::Value transactionFormatResultImpl (Transaction::pointer tpTrans) { Json::Value jvResult; try @@ -796,12 +586,67 @@ Json::Value transactionFormatResultImpl (Transaction::pointer tpTrans) //------------------------------------------------------------------------------ +Json::Value checkFee ( + Json::Value& request, + Role const role, + bool doAutoFill, + LoadFeeTrack const& feeTrack, + std::shared_ptr& ledger) +{ + Json::Value& tx (request[jss::tx_json]); + if (tx.isMember (jss::Fee)) + return Json::Value(); + + if (! doAutoFill) + return RPC::missing_field_error ("tx_json.Fee"); + + int mult = Tuning::defaultAutoFillFeeMultiplier; + if (request.isMember (jss::fee_mult_max)) + { + if (request[jss::fee_mult_max].isNumeric ()) + { + mult = request[jss::fee_mult_max].asInt(); + } + else + { + return RPC::make_error (rpcHIGH_FEE, + RPC::expected_field_message (jss::fee_mult_max, "a number")); + } + } + + // Default fee in fee units. + std::uint64_t const feeDefault = getConfig().TRANSACTION_FEE_BASE; + + // Administrative endpoints are exempt from local fees. + std::uint64_t const fee = + feeTrack.scaleFeeLoad (feeDefault, + ledger->fees().base, ledger->fees().units, role == Role::ADMIN); + + std::uint64_t const limit = mult * feeTrack.scaleFeeBase ( + feeDefault, ledger->fees().base, ledger->fees().units); + + if (fee > limit) + { + std::stringstream ss; + ss << "Fee of " << fee + << " exceeds the requested tx limit of " << limit; + return RPC::make_error (rpcHIGH_FEE, ss.str()); + } + + tx [jss::Fee] = static_cast(fee); + return Json::Value(); +} + +//------------------------------------------------------------------------------ + /** Returns a Json::objectValue. */ Json::Value transactionSign ( Json::Value jvRequest, NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role) + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger) { WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << jvRequest; @@ -809,8 +654,8 @@ Json::Value transactionSign ( // Add and amend fields based on the transaction type. SigningForParams signForParams; - transactionPreProcessResult preprocResult = - transactionPreProcessImpl (jvRequest, apiFacade, role, signForParams); + transactionPreProcessResult preprocResult = transactionPreProcessImpl ( + jvRequest, role, signForParams, validatedLedgerAge, feeTrack, ledger); if (!preprocResult.second) return preprocResult.first; @@ -829,8 +674,11 @@ Json::Value transactionSign ( Json::Value transactionSubmit ( Json::Value jvRequest, NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role) + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger, + ProcessTransactionFn const& processTransaction) { WriteLog (lsDEBUG, RPCHandler) << "transactionSubmit: " << jvRequest; @@ -838,8 +686,8 @@ Json::Value transactionSubmit ( // Add and amend fields based on the transaction type. SigningForParams signForParams; - transactionPreProcessResult preprocResult = - transactionPreProcessImpl (jvRequest, apiFacade, role, signForParams); + transactionPreProcessResult preprocResult = transactionPreProcessImpl ( + jvRequest, role, signForParams, validatedLedgerAge, feeTrack, ledger); if (!preprocResult.second) return preprocResult.first; @@ -855,7 +703,7 @@ Json::Value transactionSubmit ( try { // FIXME: For performance, should use asynch interface - apiFacade.processTransaction ( + processTransaction ( txn.second, role == Role::ADMIN, true, failType); } catch (std::exception&) @@ -900,8 +748,10 @@ Json::Value checkMultiSignFields (Json::Value const& jvRequest) Json::Value transactionSignFor ( Json::Value jvRequest, NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role) + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger) { WriteLog (lsDEBUG, RPCHandler) << "transactionSignFor: " << jvRequest; @@ -935,20 +785,25 @@ Json::Value transactionSignFor ( SigningForParams signForParams( *signerAccountID, multiSignPubKey, multiSignature); - transactionPreProcessResult preprocResult = - transactionPreProcessImpl ( - jvRequest, - apiFacade, - role, - signForParams); + transactionPreProcessResult preprocResult = transactionPreProcessImpl ( + jvRequest, + role, + signForParams, + validatedLedgerAge, + feeTrack, + ledger); if (!preprocResult.second) return preprocResult.first; // Make sure the multiSignAddrID can legitimately multi-sign. { + // Make sure the account and secret belong together. + std::shared_ptr sle = cachedRead(*ledger, + keylet::account(*signerAccountID).key, ltACCOUNT_ROOT); + error_code_i const err = - apiFacade.multiAcctMatchesPubKey (*signerAccountID, multiSignPubKey); + acctMatchesPubKey (sle, *signerAccountID, multiSignPubKey); if (err != rpcSUCCESS) return rpcError (err); @@ -993,8 +848,11 @@ Json::Value transactionSignFor ( Json::Value transactionSubmitMultiSigned ( Json::Value jvRequest, NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role) + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger, + ProcessTransactionFn const& processTransaction) { WriteLog (lsDEBUG, RPCHandler) << "transactionSubmitMultiSigned: " << jvRequest; @@ -1010,16 +868,19 @@ Json::Value transactionSubmitMultiSigned ( Json::Value& tx_json (jvRequest ["tx_json"]); - auto const txJsonResult = checkTxJsonFields(tx_json, apiFacade, role, true); + auto const txJsonResult = + checkTxJsonFields (tx_json, role, true, validatedLedgerAge, feeTrack); if (RPC::contains_error (txJsonResult.first)) return std::move (txJsonResult.first); auto const srcAddressID = txJsonResult.second; - apiFacade.snapshotAccountState (srcAddressID); - if (!apiFacade.isValidAccount ()) + std::shared_ptr sle = cachedRead(*ledger, + keylet::account(srcAddressID).key, ltACCOUNT_ROOT); + + if (!sle) { - // If not offline and did not find account, error. + // If did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSubmitMultiSigned: Failed to find source account " << "in current ledger: " @@ -1029,7 +890,8 @@ Json::Value transactionSubmitMultiSigned ( } { - Json::Value err = checkFee (jvRequest, apiFacade, role, AutoFill::dont); + Json::Value err = checkFee (jvRequest, role, false, feeTrack, ledger); + if (RPC::contains_error(err)) return std::move (err); @@ -1037,9 +899,9 @@ Json::Value transactionSubmitMultiSigned ( jvRequest, tx_json, srcAddressID, - apiFacade, role, - PathFind::dont); + ledger, + false); if (RPC::contains_error(err)) return std::move (err); @@ -1121,7 +983,7 @@ Json::Value transactionSubmitMultiSigned ( { std::ostringstream err; err << "Expected " - << signersArrayName << " to be an array"; + << signersArrayName << " to be an array."; return RPC::make_param_error (err.str ()); } @@ -1160,8 +1022,7 @@ Json::Value transactionSubmitMultiSigned ( { std::ostringstream err; err << "Duplicate Signers:Signer:Account entries (" - << getApp().accountIDCache().toBase58( - dupIter->getAccountID(sfAccount)) + << toBase58(dupIter->getAccountID(sfAccount)) << ") are not allowed."; return RPC::make_param_error(err.str ()); } @@ -1175,7 +1036,7 @@ Json::Value transactionSubmitMultiSigned ( { std::ostringstream err; err << "A Signer may not be the transaction's Account (" - << getApp().accountIDCache().toBase58(srcAddressID) << ")."; + << toBase58(srcAddressID) << ")."; return RPC::make_param_error(err.str ()); } @@ -1193,7 +1054,7 @@ Json::Value transactionSubmitMultiSigned ( try { // FIXME: For performance, should use asynch interface - apiFacade.processTransaction ( + processTransaction ( txn.second, role == Role::ADMIN, true, failType); } catch (std::exception&) diff --git a/src/ripple/rpc/impl/TransactionSign.h b/src/ripple/rpc/impl/TransactionSign.h index dcbe093fb..a7fa931ea 100644 --- a/src/ripple/rpc/impl/TransactionSign.h +++ b/src/ripple/rpc/impl/TransactionSign.h @@ -21,180 +21,94 @@ #define RIPPLE_RPC_TRANSACTIONSIGN_H_INCLUDED #include -#include #include namespace ripple { + +// Forward declarations +class LoadFeeTrack; + namespace RPC { -namespace detail { +/** Fill in the fee on behalf of the client. + This is called when the client does not explicitly specify the fee. + The client may also put a ceiling on the amount of the fee. This ceiling + is expressed as a multiplier based on the current ledger's fee schedule. -// A class that allows these methods to be called with or without a -// real NetworkOPs instance. This allows for unit testing. -class TxnSignApiFacade -{ -private: - NetworkOPs* const netOPs_; - std::shared_ptr ledger_; - AccountID accountID_; - std::shared_ptr sle_; + JSON fields -public: - // Enum used to construct a Facade for unit tests. - enum NoNetworkOPs{ - noNetOPs - }; + "Fee" The fee paid by the transaction. Omitted when the client + wants the fee filled in. - TxnSignApiFacade () = delete; - TxnSignApiFacade (TxnSignApiFacade const&) = delete; - TxnSignApiFacade& operator= (TxnSignApiFacade const&) = delete; + "fee_mult_max" A multiplier applied to the current ledger's transaction + fee that caps the maximum the fee server should auto fill. + If this optional field is not specified, then a default + multiplier is used. - // For use in non unit testing circumstances. - explicit TxnSignApiFacade (NetworkOPs& netOPs) - : netOPs_ (&netOPs) - { } - - // For testTransactionRPC unit tests. - explicit TxnSignApiFacade (NoNetworkOPs noOPs) - : netOPs_ (nullptr) { } - - // For testAutoFillFees unit tests. - TxnSignApiFacade (NoNetworkOPs noOPs, std::shared_ptr ledger) - : netOPs_ (nullptr) - , ledger_ (ledger) - { } - - void snapshotAccountState (AccountID const& accountID); - - bool isValidAccount () const; - - std::uint32_t getSeq () const; - - boost::optional - findPathsForOneIssuer ( - AccountID const& dstAccountID, - Issue const& srcIssue, - STAmount const& dstAmount, - int searchLevel, - unsigned int const maxPaths, - STPathSet const& paths, - STPath& fullLiquidityPath) const; - - void processTransaction ( - Transaction::pointer& transaction, - bool bAdmin, - bool bLocal, - NetworkOPs::FailHard failType); - - std::uint64_t scaleFeeBase (std::uint64_t fee) const; - - std::uint64_t scaleFeeLoad (std::uint64_t fee, bool bAdmin) const; - - bool hasAccountRoot () const; - - error_code_i singleAcctMatchesPubKey ( - RippleAddress const& publicKey) const; - - error_code_i multiAcctMatchesPubKey ( - AccountID const& acctID, - RippleAddress const& publicKey) const; - - int getValidatedLedgerAge () const; - - bool isLoadedCluster () const; -}; - -// A function to auto-fill fees. -enum class AutoFill : unsigned char -{ - dont, - might -}; + @param tx The JSON corresponding to the transaction to fill in. + @param ledger A ledger for retrieving the current fee schedule. + @param roll Identifies if this is called by an administrative endpoint. + @return A JSON object containing the error results, if any +*/ Json::Value checkFee ( Json::Value& request, - TxnSignApiFacade& apiFacade, Role const role, - AutoFill const doAutoFill); + bool doAutoFill, + LoadFeeTrack const& feeTrack, + std::shared_ptr& ledger); -} // namespace detail +// Return a std::function<> that calls NetworkOPs::processTransaction. +using ProcessTransactionFn = + std::function; +inline ProcessTransactionFn getProcessTxnFn (NetworkOPs& netOPs) +{ + return [&netOPs](Transaction::pointer& transaction, + bool bAdmin, bool bLocal, NetworkOPs::FailHard failType) + { + netOPs.processTransaction(transaction, bAdmin, bLocal, failType); + }; +} /** Returns a Json::objectValue. */ Json::Value transactionSign ( Json::Value params, // Passed by value so it can be modified locally. NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role); - -/** Returns a Json::objectValue. */ -inline -Json::Value transactionSign ( - Json::Value const& params, - NetworkOPs::FailHard failType, - NetworkOPs& netOPs, - Role role) -{ - detail::TxnSignApiFacade apiFacade (netOPs); - return transactionSign (params, failType, apiFacade, role); -} + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger); /** Returns a Json::objectValue. */ Json::Value transactionSubmit ( Json::Value params, // Passed by value so it can be modified locally. NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role); - -/** Returns a Json::objectValue. */ -inline -Json::Value transactionSubmit ( - Json::Value const& params, - NetworkOPs::FailHard failType, - NetworkOPs& netOPs, - Role role) -{ - detail::TxnSignApiFacade apiFacade (netOPs); - return transactionSubmit (params, failType, apiFacade, role); -} + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger, + ProcessTransactionFn const& processTransaction); /** Returns a Json::objectValue. */ Json::Value transactionSignFor ( Json::Value params, // Passed by value so it can be modified locally. NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role); - -/** Returns a Json::objectValue. */ -inline -Json::Value transactionSignFor ( - Json::Value const& params, - NetworkOPs::FailHard failType, - NetworkOPs& netOPs, - Role role) -{ - detail::TxnSignApiFacade apiFacade (netOPs); - return transactionSignFor (params, failType, apiFacade, role); -} + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger); /** Returns a Json::objectValue. */ Json::Value transactionSubmitMultiSigned ( Json::Value params, // Passed by value so it can be modified locally. NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role); - -/** Returns a Json::objectValue. */ -inline -Json::Value transactionSubmitMultiSigned ( - Json::Value const& params, - NetworkOPs::FailHard failType, - NetworkOPs& netOPs, - Role role) -{ - detail::TxnSignApiFacade apiFacade (netOPs); - return transactionSubmitMultiSigned (params, failType, apiFacade, role); -} + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger, + ProcessTransactionFn const& processTransaction); } // RPC } // ripple diff --git a/src/ripple/rpc/tests/JSONRPC.test.cpp b/src/ripple/rpc/tests/JSONRPC.test.cpp index 08e036303..a6db5d728 100644 --- a/src/ripple/rpc/tests/JSONRPC.test.cpp +++ b/src/ripple/rpc/tests/JSONRPC.test.cpp @@ -18,12 +18,11 @@ //============================================================================== #include -#include -#include +#include #include -#include -#include +#include #include +#include #include namespace ripple { @@ -303,7 +302,7 @@ R"({ "Amount": { "value": "10", "currency": "USD", - "issuer": "0123456789012345678901234567890123456789" + "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4" }, "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "TransactionType": "Payment" @@ -326,7 +325,7 @@ R"({ "Amount": { "value": "10", "currency": "USD", - "issuer": "0123456789012345678901234567890123456789" + "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4" }, "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "Paths": "", @@ -350,12 +349,12 @@ R"({ "Amount": { "value": "10", "currency": "USD", - "issuer": "0123456789012345678901234567890123456789" + "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4" }, "SendMax": { "value": "5", "currency": "USD", - "issuer": "0123456789012345678901234567890123456789" + "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4" }, "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", "TransactionType": "Payment" @@ -378,7 +377,7 @@ R"({ "Amount": { "value": "10", "currency": "USD", - "issuer": "0123456789012345678901234567890123456789" + "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4" }, "SendMax": 10000, "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", @@ -663,8 +662,8 @@ R"({ } })", { -"", -"", +"Secret does not match account.", +"Secret does not match account.", "", "Missing field 'Signers'."}}, @@ -743,8 +742,8 @@ R"({ } })", { -"", -"", +"Secret does not match account.", +"Secret does not match account.", "Missing field 'tx_json.Fee'.", "Missing field 'tx_json.Fee'."}}, @@ -763,8 +762,8 @@ R"({ } })", { -"", -"", +"Secret does not match account.", +"Secret does not match account.", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}, @@ -783,8 +782,8 @@ R"({ } })", { -"", -"", +"Secret does not match account.", +"Secret does not match account.", "Missing field 'tx_json.SigningPubKey'.", "Missing field 'tx_json.SigningPubKey'."}}, @@ -804,8 +803,8 @@ R"({ } })", { -"", -"", +"Secret does not match account.", +"Secret does not match account.", "When multi-signing 'tx_json.SigningPubKey' must be empty.", "When multi-signing 'tx_json.SigningPubKey' must be empty."}}, @@ -857,6 +856,538 @@ R"({ "Missing field 'account'.", ""}}, +{ "Missing tx_json in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ] +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json'."}}, + +{ "Missing sequence in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.Sequence'."}}, + +{ "Missing SigningPubKey in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.SigningPubKey'."}}, + +{ "Non-empty SigningPubKey in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"When multi-signing 'tx_json.SigningPubKey' must be empty."}}, + +{ "Missing TransactionType in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.TransactionType'."}}, + +{ "Missing Account in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.Account'."}}, + +{ "Malformed Account in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "NotAnAccount", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Invalid field 'tx_json.Account'."}}, + +{ "Account not in ledger in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Source account not found."}}, + +{ "Missing Fee in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.Fee'."}}, + +{ "Non-numeric Fee in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50.1, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Field 'tx_json.Fee' has invalid data."}}, + +{ "Missing Amount in submit_multisigned Payment.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50000000, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.Amount'."}}, + +{ "Invalid Amount in submit_multisigned Payment.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "NotANumber", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Invalid field 'tx_json.Amount'."}}, + +{ "No build_path in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "build_path": 1, + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Field 'build_path' not allowed in this context."}}, + +{ "Missing Destination in submit_multisigned Payment.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'tx_json.Destination'."}}, + +{ "Malformed Destination in submit_multisigned Payment.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "NotADestination", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Invalid field 'tx_json.Destination'."}}, + +{ "Missing Signers field in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Missing field 'Signers'."}}, + +{ "Signers not an array in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + }, + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Expected Signers to be an array."}}, + +{ "Empty Signers array in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Signers array may not be empty."}}, + +{ "Duplicate Signer in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + }, + { + "Signer": { + "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"Duplicate Signers:Signer:Account entries (rPcNzota6B8YBokhYtcTNqQVCngtbnWfux) are not allowed."}}, + +{ "Signer is tx_json Account in submit_multisigned.", +R"({ + "command": "submit_multisigned", + "Signers": [ + { + "Signer": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE", + "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8" + } + } + ], + "tx_json": { + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "Amount": "1000000000", + "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA", + "Fee": 50, + "Sequence": 0, + "SigningPubKey": "", + "TransactionType": "Payment" + } +})", +{ +"Missing field 'secret'.", +"Missing field 'secret'.", +"Missing field 'account'.", +"A Signer may not be the transaction's Account (rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh)."}}, + }; @@ -866,19 +1397,16 @@ public: void testAutoFillFees () { Config const config; - auto const ledger = - std::make_shared( - create_genesis, config); - - using namespace detail; - TxnSignApiFacade apiFacade (TxnSignApiFacade::noNetOPs, ledger); + std::shared_ptr ledger = + std::make_shared(create_genesis, config); + LoadFeeTrack const feeTrack; { Json::Value req; Json::Reader ().parse ( "{ \"fee_mult_max\" : 1, \"tx_json\" : { } } ", req); Json::Value result = - checkFee (req, apiFacade, Role::ADMIN, AutoFill::might); + checkFee (req, Role::ADMIN, true, feeTrack, ledger); expect (! RPC::contains_error (result), "Legal checkFee"); } @@ -888,30 +1416,72 @@ public: Json::Reader ().parse ( "{ \"fee_mult_max\" : 0, \"tx_json\" : { } } ", req); Json::Value result = - checkFee (req, apiFacade, Role::ADMIN, AutoFill::might); + checkFee (req, Role::ADMIN, true, feeTrack, ledger); expect (RPC::contains_error (result), "Invalid checkFee"); } } + // A function that can be called as though it would process a transaction. + static void fakeProcessTransaction ( + Transaction::pointer&, bool, bool, NetworkOPs::FailHard) + { + ; + } + void testTransactionRPC () { - // A list of all the functions we want to test and their fail bits. - using transactionFunc = Json::Value (*) ( + // Use jtx to set up a ledger so the tests will do the right thing. + test::jtx::Account const a {"a"}; // rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA + test::jtx::Account const g {"g"}; // rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4 + auto const USD = g["USD"]; + // master is rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh. + // "b" (not in the ledger) is rDg53Haik2475DJx8bjMDSDPj4VX7htaMd. + // "c" (phantom signer) is rPcNzota6B8YBokhYtcTNqQVCngtbnWfux. + + test::jtx::Env env(*this); + env.fund(test::jtx::XRP(100000), a, g); + env.close(); + + env(trust(a, USD(1000))); + env(trust(env.master, USD(1000))); + env(pay(g, a, USD(50))); + env(pay(g, env.master, USD(50))); + env.close(); + + auto const ledger = env.open(); + + LoadFeeTrack const feeTrack; + + ProcessTransactionFn processTxn = fakeProcessTransaction; + + // A list of all the functions we want to test. + using signFunc = Json::Value (*) ( Json::Value params, NetworkOPs::FailHard failType, - detail::TxnSignApiFacade& apiFacade, - Role role); + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger); + + using submitFunc = Json::Value (*) ( + Json::Value params, + NetworkOPs::FailHard failType, + Role role, + int validatedLedgerAge, + LoadFeeTrack const& feeTrack, + std::shared_ptr ledger, + ProcessTransactionFn const& processTransaction); using TestStuff = - std::tuple ; + std::tuple ; static TestStuff const testFuncs [] = { - TestStuff {transactionSign, "sign", 0}, - TestStuff {transactionSubmit, "submit", 1}, - TestStuff {transactionSignFor, "sign_for", 2}, - TestStuff {transactionSubmitMultiSigned, "submit_multisigned", 3} + TestStuff {transactionSign, nullptr, "sign", 0}, + TestStuff {nullptr, transactionSubmit, "submit", 1}, + TestStuff {transactionSignFor, nullptr, "sign_for", 2}, + TestStuff {nullptr, transactionSubmitMultiSigned, "submit_multisigned", 3} }; for (auto testFunc : testFuncs) @@ -930,24 +1500,41 @@ public: for (Role testRole : testedRoles) { - // Mock so we can run without a ledger. - detail::TxnSignApiFacade apiFacade ( - detail::TxnSignApiFacade::noNetOPs); - - Json::Value result = get<0>(testFunc) ( - req, - NetworkOPs::FailHard::yes, - apiFacade, - testRole); + Json::Value result; + auto const signFn = get<0>(testFunc); + if (signFn != nullptr) + { + assert (get<1>(testFunc) == nullptr); + result = signFn ( + req, + NetworkOPs::FailHard::yes, + testRole, + 1, + feeTrack, + ledger); + } + else + { + auto const submitFn = get<1>(testFunc); + assert (submitFn != nullptr); + result = submitFn ( + req, + NetworkOPs::FailHard::yes, + testRole, + 1, + feeTrack, + ledger, + processTxn); + } std::string errStr; if (RPC::contains_error (result)) errStr = result["error_message"].asString (); - std::string const expStr (txnTest.expMsg[get<2>(testFunc)]); + std::string const expStr (txnTest.expMsg[get<3>(testFunc)]); expect (errStr == expStr, "Expected: \"" + expStr + "\"\n Got: \"" + errStr + - "\"\nIn " + std::string (get<1>(testFunc)) + + "\"\nIn " + std::string (get<2>(testFunc)) + ": " + txnTest.description); } }