1#include <xrpld/app/ledger/OpenLedger.h>
2#include <xrpld/app/main/Application.h>
3#include <xrpld/app/misc/DeliverMax.h>
4#include <xrpld/app/misc/Transaction.h>
5#include <xrpld/app/misc/TxQ.h>
6#include <xrpld/rpc/detail/LegacyPathFind.h>
7#include <xrpld/rpc/detail/Pathfinder.h>
8#include <xrpld/rpc/detail/RPCHelpers.h>
9#include <xrpld/rpc/detail/TransactionSign.h>
11#include <xrpl/basics/mulDiv.h>
12#include <xrpl/core/NetworkIDService.h>
13#include <xrpl/json/json_writer.h>
14#include <xrpl/protocol/ErrorCodes.h>
15#include <xrpl/protocol/InnerObjectFormats.h>
16#include <xrpl/protocol/RPCErr.h>
17#include <xrpl/protocol/STParsedJSON.h>
18#include <xrpl/protocol/Sign.h>
19#include <xrpl/protocol/TxFlags.h>
20#include <xrpl/tx/apply.h>
21#include <xrpl/tx/applySteps.h>
82 LogicError(
"Accessing unknown SigningForParams::getSigner()");
90 LogicError(
"Accessing unknown SigningForParams::getPublicKey()");
134 bool const isMasterKey = publicKeyAcctID == accountID;
146 auto const& sle = *accountState;
149 if (sle.isFlag(lsfDisableMaster))
155 if ((sle.isFieldPresent(sfRegularKey)) && (publicKeyAcctID == sle.getAccountID(sfRegularKey)))
172 if (tx_json[jss::TransactionType].asString() != jss::Payment)
176 if (tx_json.
isMember(jss::DeliverMax))
180 if (tx_json[jss::DeliverMax] != tx_json[jss::Amount])
188 tx_json[jss::Amount] = tx_json[jss::DeliverMax];
202 if (!tx_json.
isMember(jss::Destination))
205 auto const dstAccountID = parseBase58<AccountID>(tx_json[jss::Destination].asString());
209 if (params.
isMember(jss::build_path) && ((!doPath) || amount.holds<
MPTIssue>()))
222 if (tx_json.
isMember(sfDomainID.jsonName))
225 if (!tx_json[sfDomainID.jsonName].
isString() ||
250 if (sendMax.
native() && amount.native())
320 if (!tx_json.
isMember(jss::TransactionType))
326 if (!tx_json.
isMember(jss::Account))
333 auto const srcAddressID = parseBase58<AccountID>(tx_json[jss::Account].asString());
364 ret.
second = *srcAddressID;
396static transactionPreProcessResult
416 auto const signatureTarget =
418 if (params.
isMember(jss::signature_target))
425 auto const signatureTemplate = signatureTarget
430 if (signatureTemplate ==
nullptr)
453 return std::move(txJsonResult);
468 JLOG(j.debug()) <<
"transactionSign: Failed to find source account "
469 <<
"in current ledger: " <<
toBase58(srcAddressID);
476 if (!tx_json.
isMember(jss::Sequence))
478 bool const hasTicketSeq = tx_json.
isMember(sfTicketSequence.jsonName);
479 if (!hasTicketSeq && !sle)
481 JLOG(j.debug()) <<
"transactionSign: Failed to find source account "
482 <<
"in current ledger: " <<
toBase58(srcAddressID);
489 if (!tx_json.
isMember(jss::NetworkID))
492 if (networkId > 1024)
493 tx_json[jss::NetworkID] =
to_string(networkId);
513 params, tx_json, srcAddressID, role, app,
verify && signingArgs.
editFields());
522 if (tx_json.
isMember(jss::TxnSignature))
551 if (tx_json.
isMember(sfDelegate.jsonName))
554 auto const delegateJson = tx_json[sfDelegate.jsonName];
555 auto const ptrDelegatedAddressID = delegateJson.
isString()
556 ? parseBase58<AccountID>(delegateJson.asString())
559 if (!ptrDelegatedAddressID)
565 auto delegatedAddressID = *ptrDelegatedAddressID;
587 if (!parsed.
object.has_value())
590 err[jss::error] = parsed.
error[jss::error];
591 err[jss::error_code] = parsed.
error[jss::error_code];
592 err[jss::error_message] = parsed.
error[jss::error_message];
605 if (!parsed.
object->isFieldPresent(*signatureTarget))
607 parsed.
object->setFieldObject(
608 *signatureTarget,
STObject{*signatureTemplate, *signatureTarget});
610 sigObject = &parsed.
object->peekFieldObject(*signatureTarget);
624 rpcINTERNAL,
"Exception occurred constructing serialized transaction");
642 stTx->sign(pk, sk, signatureTarget);
661 if (tpTrans->getStatus() !=
NEW)
675 tpTrans->getSTransaction()->add(s);
697 if (!tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
701 tpTrans = std::move(tpTransNew);
716 ret.
second = std::move(tpTrans);
729 jvResult[jss::hash] =
to_string(tpTrans->getID());
737 jvResult[jss::tx_json], tpTrans->getSTransaction()->getTxnType(),
apiVersion);
739 jvResult[jss::tx_blob] =
strHex(tpTrans->getSTransaction()->getSerializer().peekData());
748 jvResult[jss::engine_result] = sToken;
749 jvResult[jss::engine_result_code] = tpTrans->getResult();
750 jvResult[jss::engine_result_message] = sHuman;
777 tx[jss::Sequence] =
"0";
780 if (!tx.
isMember(jss::SigningPubKey))
782 tx[jss::SigningPubKey] =
"";
785 if (!tx.
isMember(jss::TxnSignature))
787 tx[jss::TxnSignature] =
"";
792 if (!tx[jss::Signers].isArray())
799 for (
auto& signer : tx[jss::Signers])
801 if (!signer.isMember(jss::Signer) || !signer[jss::Signer].isObject())
803 if (!signer[jss::Signer].isMember(jss::SigningPubKey))
806 signer[jss::Signer][jss::SigningPubKey] =
"";
808 if (!signer[jss::Signer].isMember(jss::TxnSignature))
811 signer[jss::Signer][jss::TxnSignature] =
"";
817 if (!parsed.
object.has_value())
856 auto const baseFee = ledger->fees().base;
857 auto escalatedFee =
toDrops(metrics.openLedgerFeeLevel -
FeeLevel64(1), baseFee) + 1;
861 auto const limit =
mulDiv(feeDefault, mult, div);
863 Throw<std::overflow_error>(
"mulDiv");
868 ss <<
"Fee of " << fee <<
" exceeds the requested tx limit of " << *limit;
894 if (request.
isMember(jss::fee_mult_max))
896 if (request[jss::fee_mult_max].isInt())
898 mult = request[jss::fee_mult_max].
asInt();
912 if (request.
isMember(jss::fee_div_max))
914 if (request[jss::fee_div_max].isInt())
916 div = request[jss::fee_div_max].
asInt();
932 if (feeOrError.isMember(jss::error))
934 tx[jss::Fee] = std::move(feeOrError);
950 using namespace detail;
953 JLOG(j.debug()) <<
"transactionSign: " << jvRequest;
956 SigningForParams signForParams;
957 transactionPreProcessResult
const preprocResult =
958 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
960 if (!preprocResult.second)
961 return preprocResult.first;
966 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
985 using namespace detail;
989 JLOG(j.debug()) <<
"transactionSubmit: " << jvRequest;
992 SigningForParams signForParams;
993 transactionPreProcessResult
const preprocResult =
994 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
996 if (!preprocResult.second)
997 return preprocResult.first;
1001 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
1026 if (!jvRequest.
isMember(jss::tx_json))
1029 Json::Value const& tx_json(jvRequest[jss::tx_json]);
1037 if (!tx_json.
isMember(jss::Sequence))
1040 if (!tx_json.
isMember(sfSigningPubKey.getJsonName()))
1046 if (!jvRequest.
isMember(jss::signature_target) &&
1050 rpcINVALID_PARAMS,
"When multi-signing 'tx_json.SigningPubKey' must be empty.");
1062 if (signers.empty())
1067 return (a[sfAccount] < b[sfAccount]);
1073 return (a[sfAccount] == b[sfAccount]);
1076 if (dupIter != signers.end())
1079 err <<
"Duplicate Signers:Signer:Account entries (" <<
toBase58((*dupIter)[sfAccount])
1080 <<
") are not allowed.";
1085 if (signers.end() !=
1087 return elem[sfAccount] == signingForID;
1091 err <<
"A Signer may not be the transaction's Account (" <<
toBase58(signingForID) <<
").";
1111 JLOG(j.debug()) <<
"transactionSignFor: " << jvRequest;
1114 char const accountField[] =
"account";
1116 if (!jvRequest.
isMember(accountField))
1120 auto const signerAccountID = parseBase58<AccountID>(jvRequest[accountField].asString());
1121 if (!signerAccountID)
1126 if (!jvRequest.
isMember(jss::tx_json))
1137 if (!tx_json.
isMember(sfSigningPubKey.getJsonName()))
1138 tx_json[sfSigningPubKey.getJsonName()] =
"";
1143 using namespace detail;
1145 Json::Value err = checkMultiSignFields(jvRequest);
1151 SigningForParams signForParams(*signerAccountID);
1153 transactionPreProcessResult
const preprocResult =
1154 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
1156 if (!preprocResult.second)
1157 return preprocResult.first;
1160 signForParams.validMultiSign(),
"xrpl::RPC::transactionSignFor : valid multi-signature");
1167 acctMatchesPubKey(account_state, *signerAccountID, signForParams.getPublicKey());
1174 auto& sttx = preprocResult.second;
1178 signer[sfAccount] = *signerAccountID;
1179 signer.
setFieldVL(sfTxnSignature, signForParams.getSignature());
1180 signer.
setFieldVL(sfSigningPubKey, signForParams.getPublicKey().slice());
1183 auto const target = signForParams.getSignatureTarget();
1192 auto& signers = sigTarget.peekFieldArray(sfSigners);
1193 signers.emplace_back(std::move(signer));
1196 auto err = sortAndValidateSigners(signers, (*sttx)[sfAccount]);
1203 transactionConstructImpl(sttx, ledger->rules(), app);
1215 unsigned apiVersion,
1224 JLOG(j.debug()) <<
"transactionSubmitMultiSigned: " << jvRequest;
1228 using namespace detail;
1230 Json::Value err = checkMultiSignFields(jvRequest);
1237 auto [txJsonResult, srcAddressID] = checkTxJsonFields(
1247 return std::move(txJsonResult);
1254 JLOG(j.debug()) <<
"transactionSubmitMultiSigned: Failed to find source account "
1255 <<
"in current ledger: " <<
toBase58(srcAddressID);
1267 err = checkPayment(jvRequest, tx_json, srcAddressID, role, app,
false);
1277 if (!parsedTx_json.
object)
1280 jvResult[
"error"] = parsedTx_json.
error[
"error"];
1281 jvResult[
"error_code"] = parsedTx_json.
error[
"error_code"];
1282 jvResult[
"error_message"] = parsedTx_json.
error[
"error_message"];
1297 rpcINTERNAL,
"Exception while serializing transaction: " + reason);
1310 if (!stTx->getFieldVL(sfSigningPubKey).empty())
1313 err <<
"Invalid " << sfSigningPubKey.fieldName
1314 <<
" field. Field must be empty when multi-signing.";
1319 if (stTx->isFieldPresent(sfTxnSignature))
1323 auto const fee = stTx->getFieldAmount(sfFee);
1328 err <<
"Invalid " << sfFee.fieldName <<
" field. Fees must be specified in XRP.";
1334 err <<
"Invalid " << sfFee.fieldName <<
" field. Fees must be greater than zero.";
1340 if (!stTx->isFieldPresent(sfSigners))
1345 auto& signers = stTx->peekFieldArray(sfSigners);
1347 if (signers.empty())
1355 obj.isFieldPresent(sfAccount) && obj.isFieldPresent(sfSigningPubKey) &&
1356 obj.isFieldPresent(sfTxnSignature) && obj.getCount() == 3);
1357 }) != signers.end())
1363 auto err = sortAndValidateSigners(signers, srcAddressID);
1369 transactionConstructImpl(stTx, ledger->rules(), app);
1385 return transactionFormatResultImpl(txn.
second, apiVersion);
T adjacent_find(T... args)
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual Config & config()=0
virtual bool checkSigs() const =0
Like std::vector<char> but better.
bool empty() const noexcept
Manages the current fee schedule.
bool isLoadedCluster() const
virtual std::uint32_t getNetworkID() const noexcept=0
Get the configured network ID.
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
Calculates payment paths.
STPathSet getBestPaths(int maxPaths, STPath &fullLiquidityPath, STPathSet const &extraPaths, AccountID const &srcIssuer, std::function< bool(void)> const &continueCallback={})
bool findPaths(int searchLevel, std::function< bool(void)> const &continueCallback={})
void computePathRanks(int maxPaths, std::function< bool(void)> const &continueCallback={})
Compute the rankings of the paths.
Slice slice() const noexcept
void setSignatureTarget(std::optional< std::reference_wrapper< SField const > > const &field)
std::optional< PublicKey > multiSignPublicKey_
bool validMultiSign() const
AccountID const & getSigner() const
bool isSingleSigning() const
std::optional< std::reference_wrapper< SField const > > const & getSignatureTarget() const
void setPublicKey(PublicKey const &multiSignPublicKey)
PublicKey const & getPublicKey() const
bool isMultiSigning() const
Buffer const & getSignature() const
SigningForParams(AccountID const &multiSigningAcctID)
void moveMultiSignature(Buffer &&multiSignature)
std::optional< std::reference_wrapper< SField const > > signatureTarget_
AccountID const *const multiSigningAcctID_
SigningForParams(SigningForParams const &rhs)=delete
Rules controlling protocol behavior.
static SField const & getField(int fieldCode)
Issue const & issue() const
void setIssuer(AccountID const &uIssuer)
bool native() const noexcept
void setFieldVL(SField const &field, Blob const &)
void setFieldArray(SField const &field, STArray const &v)
STObject & peekFieldObject(SField const &field)
bool isFieldPresent(SField const &field) const
static STObject makeInnerObject(SField const &name)
Holds the serialized result of parsing an input JSON object.
std::optional< STObject > object
The STObject if the parse was successful.
Json::Value error
On failure, an appropriate set of error values.
Json::Value getJson(JsonOptions) const override
static constexpr std::size_t maxMultiSigners
constexpr std::uint32_t value() const
Slice slice() const noexcept
virtual beast::Journal getJournal(std::string const &name)=0
virtual OpenLedger & getOpenLedger()=0
virtual NetworkIDService & getNetworkIDService()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual HashRouter & getHashRouter()=0
An immutable linear range of bytes.
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
SeqProxy nextQueuableSeq(std::shared_ptr< SLE const > const &sleAccount) const
Return the next sequence that would go in the TxQ for an account.
Json::Value jsonClipped() const
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
static int constexpr defaultAutoFillFeeMultiplier
auto constexpr maxValidatedLedgerAge
static int constexpr defaultAutoFillFeeDivisor
static transactionPreProcessResult transactionPreProcessImpl(Json::Value ¶ms, Role role, SigningForParams &signingArgs, std::chrono::seconds validatedLedgerAge, Application &app)
static Json::Value checkPayment(Json::Value const ¶ms, Json::Value &tx_json, AccountID const &srcAddressID, Role const role, Application &app, bool doPath)
static Json::Value transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion)
static Json::Value checkMultiSignFields(Json::Value const &jvRequest)
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, unsigned apiVersion)
static std::pair< Json::Value, Transaction::pointer > transactionConstructImpl(std::shared_ptr< STTx const > const &stTx, Rules const &rules, Application &app)
static error_code_i acctMatchesPubKey(std::shared_ptr< SLE const > accountState, AccountID const &accountID, PublicKey const &publicKey)
static Json::Value sortAndValidateSigners(STArray &signers, AccountID const &signingForID)
Json::Value invalid_field_error(std::string const &name)
std::string expected_field_message(std::string const &name, std::string const &type)
Json::Value transactionSubmit(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
std::string missing_field_message(std::string const &name)
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Json::Value transactionSign(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
Json::Value missing_field_error(std::string const &name)
static XRPAmount getTxFee(Application const &app, Config const &config, Json::Value tx)
Json::Value getCurrentNetworkFee(Role const role, Config const &config, LoadFeeTrack const &feeTrack, TxQ const &txQ, Application const &app, Json::Value const &tx, int mult, int div)
Json::Value object_field_error(std::string const &name)
Json::Value checkFee(Json::Value &request, Role const role, bool doAutoFill, Config const &config, LoadFeeTrack const &feeTrack, TxQ const &txQ, Application const &app)
Fill in the fee on behalf of the client.
Json::Value transactionSignFor(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
static constexpr std::integral_constant< unsigned, Version > apiVersion
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Json::Value transactionSubmitMultiSigned(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
std::string invalid_field_message(std::string const &name)
std::optional< std::pair< PublicKey, SecretKey > > keypairForSignature(Json::Value const ¶ms, Json::Value &error, unsigned int apiVersion)
Generates a keypair for signature from RPC parameters.
unsigned int getAPIVersionNumber(Json::Value const &jv, bool betaEnabled)
Retrieve the api version number from the json value.
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::optional< std::uint64_t > mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
Return value*mul/div accurately.
@ Valid
Signature and local checks are good / passed.
@ SigGoodOnly
Signature is good, but local checks fail.
std::string to_string(base_uint< Bits, Tag > const &a)
std::string strHex(FwdIt begin, FwdIt end)
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules)
Checks transaction signature and local checks.
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
bool isLegalNet(STAmount const &value)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
bool transResultInfo(TER code, std::string &token, std::string &text)
Role
Indicates the level of administrative permission to grant.
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
bool passesLocalChecks(STObject const &st, std::string &)
AccountID calcAccountID(PublicKey const &pk)
Json::Value rpcError(error_code_i iError)
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
Buffer sign(PublicKey const &pk, SecretKey const &sk, Slice const &message)
Generate a signature for a message.
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
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)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
@ rpcDELEGATE_ACT_NOT_FOUND
Serializer buildMultiSigningData(STObject const &obj, AccountID const &signingID)
Return a Serializer suitable for computing a multisigning TxnSignature.
XRPAmount reference_fee
The cost of a reference transaction in drops.
transactionPreProcessResult & operator=(transactionPreProcessResult &&)=delete
transactionPreProcessResult(std::shared_ptr< STTx > &&st)
transactionPreProcessResult & operator=(transactionPreProcessResult const &)=delete
transactionPreProcessResult(Json::Value &&json)
transactionPreProcessResult(transactionPreProcessResult &&rhs)=default
transactionPreProcessResult()=delete
transactionPreProcessResult(transactionPreProcessResult const &)=delete
std::shared_ptr< STTx > const second