20 #include <ripple/rpc/impl/TransactionSign.h>
21 #include <ripple/app/ledger/LedgerMaster.h>
22 #include <ripple/app/ledger/OpenLedger.h>
23 #include <ripple/app/main/Application.h>
24 #include <ripple/app/misc/LoadFeeTrack.h>
25 #include <ripple/app/misc/Transaction.h>
26 #include <ripple/app/misc/TxQ.h>
27 #include <ripple/app/paths/Pathfinder.h>
28 #include <ripple/app/tx/apply.h>
29 #include <ripple/basics/Log.h>
30 #include <ripple/basics/mulDiv.h>
31 #include <ripple/json/json_writer.h>
32 #include <ripple/net/RPCErr.h>
33 #include <ripple/protocol/Sign.h>
34 #include <ripple/protocol/ErrorCodes.h>
35 #include <ripple/protocol/Feature.h>
36 #include <ripple/protocol/STAccount.h>
37 #include <ripple/protocol/STParsedJSON.h>
38 #include <ripple/protocol/TxFlags.h>
39 #include <ripple/rpc/impl/LegacyPathFind.h>
40 #include <ripple/rpc/impl/RPCHelpers.h>
41 #include <ripple/rpc/impl/Tuning.h>
119 bool const isMasterKey = publicKeyAcctID == accountID;
131 auto const& sle = *accountState;
158 if (tx_json[jss::TransactionType].asString () != jss::Payment)
161 if (!tx_json.
isMember (jss::Amount))
169 if (!tx_json.
isMember (jss::Destination))
172 auto const dstAccountID = parseBase58<AccountID>(
173 tx_json[jss::Destination].asString());
177 if ((doPath ==
false) && params.
isMember (jss::build_path))
179 "Field 'build_path' not allowed in this context.");
183 "Cannot specify both 'tx_json.Paths' and 'build_path'");
189 if (tx_json.
isMember (jss::SendMax))
203 "Cannot build XRP to XRP paths.");
213 Pathfinder pf(std::make_shared<RippleLineCache>(ledger),
227 auto j = app.
journal (
"RPCHandler");
229 <<
"transactionSign: build_path: "
232 if (! result.
empty ())
266 if (! tx_json.
isMember (jss::TransactionType))
272 if (! tx_json.
isMember (jss::Account))
279 auto const srcAddressID = parseBase58<AccountID>(
280 tx_json[jss::Account].asString());
305 ret.
second = *srcAddressID;
342 transactionPreProcessResult
351 auto j = app.
journal (
"RPCHandler");
359 && params[jss::offline].
asBool());
361 if (! params.
isMember (jss::tx_json))
368 tx_json, role,
verify, validatedLedgerAge,
372 return std::move (txJsonResult);
387 <<
"transactionSign: Failed to find source account "
388 <<
"in current ledger: "
422 if (!tx_json.
isMember (jss::Sequence))
427 <<
"transactionSign: Failed to find source account "
428 <<
"in current ledger: "
439 for(
auto const& tx : queued)
443 else if (tx.first > seq)
446 tx_json[jss::Sequence] = seq;
484 sle, srcAddressID, pk);
492 if (parsed.
object == boost::none)
495 err [jss::error] = parsed.
error [jss::error];
496 err [jss::error_code] = parsed.
error [jss::error_code];
497 err [jss::error_message] = parsed.
error [jss::error_message];
511 stpTrans = std::make_shared<STTx> (
512 std::move (parsed.
object.get()));
521 "Exception occurred constructing serialized transaction");
543 stpTrans->sign (pk, sk);
560 tpTrans = std::make_shared<Transaction>(
561 stpTrans, reason, app);
562 if (tpTrans->getStatus () !=
NEW)
565 "Unable to construct transaction: " + reason);
577 tpTrans->getSTransaction ()->add (s);
582 auto sttxNew = std::make_shared<STTx const> (sit);
590 "Invalid signature.");
596 std::make_shared<Transaction> (sttxNew, reason, app);
600 if (!tpTransNew->getSTransaction()->isEquivalent (
601 *tpTrans->getSTransaction()))
605 tpTrans = std::move (tpTransNew);
618 "Unable to sterilize transaction.");
621 ret.
second = std::move (tpTrans);
631 jvResult[jss::tx_blob] =
strHex (
632 tpTrans->getSTransaction ()->getSerializer ().peekData ());
641 jvResult[jss::engine_result] = sToken;
642 jvResult[jss::engine_result_code] = tpTrans->getResult ();
643 jvResult[jss::engine_result_message] = sHuman;
649 "Exception occurred during JSON handling.");
676 if (request.
isMember (jss::fee_mult_max))
678 if (request[jss::fee_mult_max].isInt())
680 mult = request[jss::fee_mult_max].
asInt();
684 "a positive integer"));
690 "a positive integer"));
693 if (request.
isMember(jss::fee_div_max))
695 if (request[jss::fee_div_max].isInt())
697 div = request[jss::fee_div_max].
asInt();
701 "a positive integer"));
707 "a positive integer"));
721 auto const baseFee = ledger->fees().base;
723 metrics.openLedgerFeeLevel -
FeeLevel64{ 1 }, baseFee).second +
728 auto const limit = [&]()
731 auto const drops =
mulDiv (feeDefault,
732 ledger->fees().base, ledger->fees().units);
734 Throw<std::overflow_error>(
"mulDiv");
735 auto const result =
mulDiv (drops.second, mult, div);
737 Throw<std::overflow_error>(
"mulDiv");
738 return result.second;
744 ss <<
"Fee of " << fee
745 <<
" exceeds the requested tx limit of " << limit;
763 using namespace detail;
766 auto j = app.
journal (
"RPCHandler");
767 JLOG (j.debug()) <<
"transactionSign: " << jvRequest;
770 SigningForParams signForParams;
772 jvRequest, role, signForParams,
773 validatedLedgerAge, app, ledger);
775 if (!preprocResult.second)
776 return preprocResult.first;
781 preprocResult.second, ledger->rules(), app);
798 using namespace detail;
801 auto j = app.
journal (
"RPCHandler");
802 JLOG (j.debug()) <<
"transactionSubmit: " << jvRequest;
806 SigningForParams signForParams;
808 jvRequest, role, signForParams, validatedLedgerAge, app, ledger);
810 if (!preprocResult.second)
811 return preprocResult.first;
816 preprocResult.second, ledger->rules(), app);
831 "Exception occurred during transaction submission.");
843 if (! jvRequest.
isMember (jss::tx_json))
846 Json::Value const& tx_json (jvRequest [jss::tx_json]);
854 if (!tx_json.
isMember (jss::Sequence))
862 "When multi-signing 'tx_json.SigningPubKey' must be empty.");
873 if (signers.empty ())
880 return (a[sfAccount] < b[sfAccount]);
888 return (a[sfAccount] == b[sfAccount]);
891 if (dupIter != signers.
end())
894 err <<
"Duplicate Signers:Signer:Account entries ("
896 <<
") are not allowed.";
902 [&signingForID](
STObject const& elem)
904 return elem[sfAccount] == signingForID;
908 err <<
"A Signer may not be the transaction's Account ("
926 auto j = app.
journal (
"RPCHandler");
927 JLOG (j.debug()) <<
"transactionSignFor: " << jvRequest;
930 const char accountField[] =
"account";
932 if (! jvRequest.
isMember (accountField))
936 auto const signerAccountID = parseBase58<AccountID>(
937 jvRequest[accountField].asString());
938 if (! signerAccountID)
944 if (! jvRequest.
isMember (jss::tx_json))
961 using namespace detail;
971 SigningForParams signForParams(
972 *signerAccountID, multiSignPubKey, multiSignature);
982 if (!preprocResult.second)
983 return preprocResult.first;
999 auto& sttx = preprocResult.second;
1011 auto& signers = sttx->peekFieldArray (
sfSigners);
1012 signers.emplace_back (std::move (signer));
1040 auto j = app.
journal (
"RPCHandler");
1042 <<
"transactionSubmitMultiSigned: " << jvRequest;
1046 using namespace detail;
1056 tx_json, role,
true, validatedLedgerAge,
1060 return std::move (txJsonResult);
1069 <<
"transactionSubmitMultiSigned: Failed to find source account "
1070 <<
"in current ledger: "
1101 if (!parsedTx_json.
object)
1104 jvResult [
"error"] = parsedTx_json.
error [
"error"];
1105 jvResult [
"error_code"] = parsedTx_json.
error [
"error_code"];
1106 jvResult [
"error_message"] = parsedTx_json.
error [
"error_message"];
1111 stpTrans = std::make_shared<STTx>(
1112 std::move(parsedTx_json.
object.get()));
1122 "Exception while serializing transaction: " + reason);
1139 <<
" field. Field must be empty when multi-signing.";
1148 auto const fee = stpTrans->getFieldAmount (
sfFee);
1154 <<
" field. Fees must be specified in XRP.";
1161 <<
" field. Fees must be greater than zero.";
1167 if (! stpTrans->isFieldPresent (
sfSigners))
1172 auto& signers = stpTrans->peekFieldArray (
sfSigners);
1174 if (signers.empty ())
1182 obj.isFieldPresent (sfAccount) &&
1183 obj.isFieldPresent (sfSigningPubKey) &&
1184 obj.isFieldPresent (sfTxnSignature) &&
1185 obj.getCount() == 3);
1186 }) != signers.
end())
1189 "Signers array may only contain Signer entries.");
1208 processTransaction (
1214 "Exception occurred during transaction submission.");
AccountID const *const multiSigningAcctID_
virtual bool checkSigs() const =0
const SF_Account sfRegularKey(access, STI_ACCOUNT, 8, "RegularKey")
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
Json::Value transactionSignFor(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
std::string missing_field_message(std::string const &name)
Calculates payment paths.
const SField sfSigners(access, STI_ARRAY, 3, "Signers", SField::sMD_Default, SField::notSigning)
An immutable linear range of bytes.
PublicKey *const multiSignPublicKey_
const SF_Blob sfSigningPubKey(access, STI_VL, 3, "SigningPubKey")
const SF_U32 sfSequence(access, STI_UINT32, 4, "Sequence")
const std::string fieldName
const std::shared_ptr< STTx > second
const SF_Account sfAccount(access, STI_ACCOUNT, 1, "Account")
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
static Json::Value checkPayment(Json::Value const ¶ms, Json::Value &tx_json, AccountID const &srcAddressID, Role const role, Application &app, std::shared_ptr< ReadView const > const &ledger, bool doPath)
XRPAmount scaleFeeLoad(FeeUnit64 fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
Json::Value checkFee(Json::Value &request, Role const role, bool doAutoFill, Config const &config, LoadFeeTrack const &feeTrack, TxQ const &txQ, std::shared_ptr< OpenView const > const &ledger)
Fill in the fee on behalf of the client.
Slice slice() const noexcept
bool findPaths(int searchLevel)
Like std::vector<char> but better.
Holds the serialized result of parsing an input JSON object.
bool isLegalNet(STAmount const &value)
const_iterator end() const
void setFieldVL(SField const &field, Blob const &)
const SField sfSigner(access, STI_OBJECT, 16, "Signer")
void setIssuer(AccountID const &uIssuer)
Json::StaticString const & getJsonName() const
virtual OpenLedger & openLedger()=0
Json::Value transactionSign(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
const Json::StaticString jsonName
static std::pair< Json::Value, Transaction::pointer > transactionConstructImpl(std::shared_ptr< STTx const > const &stpTrans, Rules const &rules, Application &app)
Json::Value object_field_error(std::string const &name)
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
transactionPreProcessResult(std::shared_ptr< STTx > &&st)
virtual LoadFeeTrack & getFeeTrack()=0
Json::Value error
On failure, an appropriate set of error values.
void setPublicKey(PublicKey const &multiSignPublicKey)
@ SigGoodOnly
Signature is good, but local checks fail.
static error_code_i acctMatchesPubKey(std::shared_ptr< SLE const > accountState, AccountID const &accountID, PublicKey const &publicKey)
Json::Value missing_field_error(std::string const &name)
STPathSet getBestPaths(int maxPaths, STPath &fullLiquidityPath, STPathSet const &extraPaths, AccountID const &srcIssuer)
AccountID const & getSigner()
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical)
Verify a signature on a message.
static Json::Value transactionFormatResultImpl(Transaction::pointer tpTrans)
std::string expected_field_message(std::string const &name, std::string const &type)
Json::Value transactionSubmit(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules, Config const &config)
Checks transaction signature and local checks.
bool passesLocalChecks(STObject const &st, std::string &reason)
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
void computePathRanks(int maxPaths)
Compute the rankings of the paths.
static constexpr FeeUnit32 TRANSACTION_FEE_BASE
static const account_t account
virtual Config & config()=0
static Json::Value sortAndValidateSigners(STArray &signers, AccountID const &signingForID)
AccountID calcAccountID(PublicKey const &pk)
Manages the current fee schedule.
const std::uint32_t tfFullyCanonicalSig
const SF_Blob sfTxnSignature(access, STI_VL, 4, "TxnSignature", SField::sMD_Default, SField::notSigning)
Slice slice() const noexcept
std::map< TxSeq, AccountTxDetails const > getAccountTxs(AccountID const &account, ReadView const &view) const
Returns information about the transactions currently in the queue for the account.
bool isMember(const char *key) const
Return true if the object has a member named key.
boost::optional< STObject > object
The STObject if the parse was successful.
Json::Value rpcError(int iError, Json::Value jvResult)
const SF_Amount sfFee(access, STI_AMOUNT, 8, "Fee")
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
static transactionPreProcessResult transactionPreProcessImpl(Json::Value ¶ms, Role role, SigningForParams &signingArgs, std::chrono::seconds validatedLedgerAge, Application &app, std::shared_ptr< OpenView const > const &ledger)
transactionPreProcessResult(Json::Value &&json)
std::string invalid_field_message(std::string const &name)
bool native() const noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool transResultInfo(TER code, std::string &token, std::string &text)
virtual beast::Journal journal(std::string const &name)=0
@ Valid
Signature and local checks are good / passed.
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &m)
Generate a signature for a message.
Buffer *const multiSignature_
void moveMultiSignature(Buffer &&multiSignature)
Issue const & issue() const
std::pair< PublicKey, SecretKey > keypairForSignature(Json::Value const ¶ms, Json::Value &error)
std::pair< bool, XRPAmount > toDrops(FeeLevel< T > const &level, XRPAmount const &baseFee)
bool isLoadedCluster() const
Json::Value getJson(JsonOptions) const override
bool isSingleSigning() const
T adjacent_find(T... args)
bool isMultiSigning() const
Rules controlling protocol behavior.
static Json::Value checkMultiSignFields(Json::Value const &jvRequest)
transactionPreProcessResult(transactionPreProcessResult &&rhs)
SigningForParams(AccountID const &multiSigningAcctID, PublicKey &multiSignPublicKey, Buffer &multiSignature)
std::string strHex(FwdIt begin, FwdIt end)
std::pair< bool, Dest > mulDiv(Source1 value, Dest mul, Source2 div)
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
static std::pair< Json::Value, AccountID > checkTxJsonFields(Json::Value const &tx_json, Role const role, bool const verify, std::chrono::seconds validatedLedgerAge, Config const &config, LoadFeeTrack const &feeTrack)
const_iterator begin() const
static constexpr int defaultAutoFillFeeMultiplier
Json::Value invalid_field_error(std::string const &name)
virtual HashRouter & getHashRouter()=0
static constexpr int defaultAutoFillFeeDivisor
Role
Indicates the level of administrative permission to grant.
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
constexpr auto maxValidatedLedgerAge
Json::Value jsonClipped() const
Serializer buildMultiSigningData(STObject const &obj, AccountID const &signingID)
Return a Serializer suitable for computing a multisigning TxnSignature.
Json::Value transactionSubmitMultiSigned(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
transactionPreProcessResult()=delete
std::string asString() const
Returns the unquoted string value.