xrpld
Loading...
Searching...
No Matches
TransactionSign.cpp
1#include <xrpld/rpc/detail/TransactionSign.h>
2
3#include <xrpld/app/ledger/OpenLedger.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/app/misc/DeliverMax.h>
6#include <xrpld/app/misc/Transaction.h>
7#include <xrpld/app/misc/TxQ.h>
8#include <xrpld/rpc/Role.h>
9#include <xrpld/rpc/detail/AssetCache.h>
10#include <xrpld/rpc/detail/LegacyPathFind.h>
11#include <xrpld/rpc/detail/Pathfinder.h>
12#include <xrpld/rpc/detail/RPCHelpers.h>
13#include <xrpld/rpc/detail/Tuning.h>
14
15#include <xrpl/basics/Blob.h>
16#include <xrpl/basics/Buffer.h>
17#include <xrpl/basics/Log.h>
18#include <xrpl/basics/Number.h>
19#include <xrpl/basics/Slice.h>
20#include <xrpl/basics/base_uint.h>
21#include <xrpl/basics/contract.h>
22#include <xrpl/basics/strHex.h>
23#include <xrpl/beast/utility/instrumentation.h>
24#include <xrpl/core/NetworkIDService.h>
25#include <xrpl/json/json_writer.h>
26#include <xrpl/protocol/AccountID.h>
27#include <xrpl/protocol/ApiVersion.h>
28#include <xrpl/protocol/ErrorCodes.h>
29#include <xrpl/protocol/Feature.h>
30#include <xrpl/protocol/Indexes.h>
31#include <xrpl/protocol/InnerObjectFormats.h>
32#include <xrpl/protocol/Issue.h>
33#include <xrpl/protocol/LedgerFormats.h>
34#include <xrpl/protocol/MPTIssue.h>
35#include <xrpl/protocol/PublicKey.h>
36#include <xrpl/protocol/RPCErr.h>
37#include <xrpl/protocol/SField.h>
38#include <xrpl/protocol/STAmount.h>
39#include <xrpl/protocol/STArray.h>
40#include <xrpl/protocol/STObject.h>
41#include <xrpl/protocol/STParsedJSON.h>
42#include <xrpl/protocol/STPathSet.h>
43#include <xrpl/protocol/STTx.h>
44#include <xrpl/protocol/SecretKey.h>
45#include <xrpl/protocol/Serializer.h>
46#include <xrpl/protocol/Sign.h>
47#include <xrpl/protocol/TER.h>
48#include <xrpl/protocol/Units.h>
49#include <xrpl/protocol/XRPAmount.h>
50#include <xrpl/protocol/jss.h>
51#include <xrpl/server/LoadFeeTrack.h>
52#include <xrpl/tx/apply.h> // Validity::Valid
53#include <xrpl/tx/applySteps.h>
54
55#include <algorithm>
56#include <chrono>
57#include <cstdint>
58#include <exception>
59#include <expected>
60#include <functional>
61#include <memory>
62#include <optional>
63#include <sstream>
64#include <stdexcept>
65#include <utility>
66
67namespace xrpl::rpc {
68namespace detail {
69
70// Used to pass extra parameters used when returning a
71// a SigningFor object.
73{
74private:
79
80public:
82 {
83 }
84
85 SigningForParams(SigningForParams const& rhs) = delete;
86
87 SigningForParams(AccountID const& multiSigningAcctID) : multiSigningAcctID_(&multiSigningAcctID)
88 {
89 }
90
91 [[nodiscard]] bool
93 {
94 return multiSigningAcctID_ != nullptr;
95 }
96
97 [[nodiscard]] bool
99 {
100 return !isMultiSigning();
101 }
102
103 // When multi-signing we should not edit the tx_json fields.
104 [[nodiscard]] bool
106 {
107 return !isMultiSigning();
108 }
109
110 [[nodiscard]] bool
112 {
113 return isMultiSigning() && multiSignPublicKey_ && !multiSignature_.empty();
114 }
115
116 // Don't call this method unless isMultiSigning() returns true.
117 [[nodiscard]] AccountID const&
118 getSigner() const
119 {
120 if (multiSigningAcctID_ == nullptr)
121 logicError("Accessing unknown SigningForParams::getSigner()");
122 return *multiSigningAcctID_;
123 }
124
125 [[nodiscard]] PublicKey const&
127 {
129 logicError("Accessing unknown SigningForParams::getPublicKey()");
130 return *multiSignPublicKey_;
131 }
132
133 [[nodiscard]] Buffer const&
135 {
136 return multiSignature_;
137 }
138
141 {
142 return signatureTarget_;
143 }
144
145 void
146 setPublicKey(PublicKey const& multiSignPublicKey)
147 {
148 multiSignPublicKey_ = multiSignPublicKey;
149 }
150
151 void
156
157 void
158 moveMultiSignature(Buffer&& multiSignature)
159 {
160 multiSignature_ = std::move(multiSignature);
161 }
162};
163
164//------------------------------------------------------------------------------
165
166static ErrorCodeI
168 SLE::const_pointer accountState,
169 AccountID const& accountID,
170 PublicKey const& publicKey)
171{
172 auto const publicKeyAcctID = calcAccountID(publicKey);
173 bool const isMasterKey = publicKeyAcctID == accountID;
174
175 // If we can't get the accountRoot, but the accountIDs match, that's
176 // good enough.
177 if (!accountState)
178 {
179 if (isMasterKey)
180 return RpcSuccess;
181 return RpcBadSecret;
182 }
183
184 // If we *can* get to the accountRoot, check for MASTER_DISABLED.
185 auto const& sle = *accountState;
186 if (isMasterKey)
187 {
188 if (sle.isFlag(lsfDisableMaster))
189 return RpcMasterDisabled;
190 return RpcSuccess;
191 }
192
193 // The last gasp is that we have public Regular key.
194 if ((sle.isFieldPresent(sfRegularKey)) && (publicKeyAcctID == sle.getAccountID(sfRegularKey)))
195 {
196 return RpcSuccess;
197 }
198 return RpcBadSecret;
199}
200
201static json::Value
203 json::Value const& params,
204 json::Value& txJson,
205 AccountID const& srcAddressID,
206 Role const role,
207 Application& app,
208 bool doPath)
209{
210 // Only path find for Payments.
211 if (txJson[jss::TransactionType].asString() != jss::Payment)
212 return json::Value();
213
214 // DeliverMax is an alias to Amount and we use Amount internally
215 if (txJson.isMember(jss::DeliverMax))
216 {
217 if (txJson.isMember(jss::Amount))
218 {
219 if (txJson[jss::DeliverMax] != txJson[jss::Amount])
220 {
221 return rpc::makeError(
222 RpcInvalidParams, "Cannot specify differing 'Amount' and 'DeliverMax'");
223 }
224 }
225 else
226 {
227 txJson[jss::Amount] = txJson[jss::DeliverMax];
228 }
229
230 txJson.removeMember(jss::DeliverMax);
231 }
232
233 if (!txJson.isMember(jss::Amount))
234 return rpc::missingFieldError("tx_json.Amount");
235
236 STAmount amount;
237
238 if (!amountFromJsonNoThrow(amount, txJson[jss::Amount]))
239 return rpc::invalidFieldError("tx_json.Amount");
240
241 if (!txJson.isMember(jss::Destination))
242 return rpc::missingFieldError("tx_json.Destination");
243
244 auto const dstAccountID = parseBase58<AccountID>(txJson[jss::Destination].asString());
245 if (!dstAccountID)
246 return rpc::invalidFieldError("tx_json.Destination");
247
248 if (params.isMember(jss::build_path) &&
249 (!doPath ||
250 (!app.getOpenLedger().current()->rules().enabled(featureMPTokensV2) &&
251 amount.holds<MPTIssue>())))
252 {
253 return rpc::makeError(RpcInvalidParams, "Field 'build_path' not allowed in this context.");
254 }
255
256 if (txJson.isMember(jss::Paths) && params.isMember(jss::build_path))
257 {
258 return rpc::makeError(
259 RpcInvalidParams, "Cannot specify both 'tx_json.Paths' and 'build_path'");
260 }
261
263 if (txJson.isMember(sfDomainID.jsonName))
264 {
265 UInt256 num;
266 if (!txJson[sfDomainID.jsonName].isString() ||
267 !num.parseHex(txJson[sfDomainID.jsonName].asString()))
268 {
269 return rpc::makeError(RpcDomainMalformed, "Unable to parse 'DomainID'.");
270 }
271
272 domain = num;
273 }
274
275 if (!txJson.isMember(jss::Paths) && params.isMember(jss::build_path))
276 {
277 STAmount sendMax;
278
279 if (txJson.isMember(jss::SendMax))
280 {
281 if (!amountFromJsonNoThrow(sendMax, txJson[jss::SendMax]))
282 return rpc::invalidFieldError("tx_json.SendMax");
283 }
284 else
285 {
286 // If no SendMax, default to Amount with sender as issuer if Issue.
287 sendMax = amount;
288 sendMax.asset().visit(
289 [&](Issue const&) { sendMax.get<Issue>().account = srcAddressID; },
290 [](MPTIssue const&) {});
291 }
292
293 if (sendMax.native() && amount.native())
294 return rpc::makeError(RpcInvalidParams, "Cannot build XRP to XRP paths.");
295
296 {
297 LegacyPathFind const lpf(isUnlimited(role), app);
298 if (!lpf.isOk())
299 return rpcError(RpcTooBusy);
300
301 STPathSet result;
302
303 if (auto ledger = app.getOpenLedger().current())
304 {
305 Pathfinder pf(
306 std::make_shared<AssetCache>(ledger, app.getJournal("AssetCache")),
307 srcAddressID,
308 *dstAccountID,
309 sendMax.asset(),
310 sendMax.getIssuer(),
311 amount,
312 std::nullopt,
313 domain,
314 app);
315 if (pf.findPaths(app.config().pathSearchOld))
316 {
317 // 4 is the maximum paths
318 pf.computePathRanks(4);
319 STPath fullLiquidityPath;
320 STPathSet const paths;
321 result = pf.getBestPaths(4, fullLiquidityPath, paths, sendMax.getIssuer());
322 }
323 }
324
325 auto j = app.getJournal("RPCHandler");
326 JLOG(j.debug()) << "transactionSign: build_path: "
328
329 if (!result.empty())
330 txJson[jss::Paths] = result.getJson(JsonOptions::Values::None);
331 }
332 }
333 return json::Value();
334}
335
336//------------------------------------------------------------------------------
337
338// Validate (but don't modify) the contents of the tx_json.
339//
340// Returns a pair<json::Value, AccountID>. The json::Value will contain error
341// information if there was an error. On success, the account ID is returned
342// and the json::Value will be empty.
343//
344// This code does not check the "Sequence" field, since the expectations
345// for that field are particularly context sensitive.
348 json::Value const& txJson,
349 Role const role,
350 bool const verify,
351 std::chrono::seconds validatedLedgerAge,
352 Config const& config,
353 LoadFeeTrack const& feeTrack,
354 unsigned apiVersion)
355{
357
358 if (!txJson.isObject())
359 {
360 ret.first = rpc::objectFieldError(jss::tx_json);
361 return ret;
362 }
363
364 if (!txJson.isMember(jss::TransactionType))
365 {
366 ret.first = rpc::missingFieldError("tx_json.TransactionType");
367 return ret;
368 }
369
370 if (!txJson.isMember(jss::Account))
371 {
373 return ret;
374 }
375
376 auto const srcAddressID = parseBase58<AccountID>(txJson[jss::Account].asString());
377
378 if (!srcAddressID)
379 {
381 return ret;
382 }
383
384 // Check for current ledger.
385 if (verify && !config.standalone() && (validatedLedgerAge > tuning::kMaxValidatedLedgerAge))
386 {
387 if (apiVersion == 1)
388 {
390 }
391 else
392 {
394 }
395 return ret;
396 }
397
398 // Check for load.
399 if (feeTrack.isLoadedCluster() && !isUnlimited(role))
400 {
402 return ret;
403 }
404
405 // It's all good. Return the AccountID.
406 ret.second = *srcAddressID;
407 return ret;
408}
409
410static std::expected<void, json::Value>
411checkNetworkID(json::Value const& txJson, uint32_t appNetworkId)
412{
413 if (appNetworkId > 1024)
414 {
415 if (!txJson.isMember(jss::NetworkID))
416 {
417 return std::unexpected(
419 }
420 if (!txJson[jss::NetworkID].isIntegral() || txJson[jss::NetworkID].asUInt() != appNetworkId)
421 {
422 return std::unexpected(
424 }
425 }
426 return std::expected<void, json::Value>();
427}
428
429//------------------------------------------------------------------------------
430
431// A move-only struct that makes it easy to return either a json::Value or a
432// std::shared_ptr<STTx const> from transactionPreProcessImpl ().
456
457static TransactionPreProcessResult
459 json::Value& params,
460 Role role,
461 SigningForParams& signingArgs,
462 std::chrono::seconds validatedLedgerAge,
463 Application& app,
464 Rules const& rules)
465{
466 auto j = app.getJournal("RPCHandler");
467
468 json::Value jvResult;
470 if (!keyPair || containsError(jvResult))
471 return jvResult;
472
473 PublicKey const& pk = keyPair->first;
474 SecretKey const& sk = keyPair->second;
475
476 bool const verify = !(params.isMember(jss::offline) && params[jss::offline].asBool());
477
478 auto const signatureTarget =
480 if (params.isMember(jss::signature_target))
481 return SField::getField(params[jss::signature_target].asString());
482 return std::nullopt;
483 }();
484
485 // Make sure the signature target field is valid, if specified, and save the
486 // template for use later. Only a field that holds a transaction signature
487 // is a valid target; the signature is bound to that field's role.
488 auto const signatureTemplate = signatureTarget
490 : nullptr;
491 auto const signatureRoleOpt =
492 signatureTarget ? signatureRole(signatureTarget->get()) : SignatureRole::Transaction;
493 if (signatureTarget)
494 {
495 if (!signatureRoleOpt || signatureTemplate == nullptr)
496 { // Invalid target field
497 return rpc::makeError(RpcInvalidParams, signatureTarget->get().getName());
498 }
499 signingArgs.setSignatureTarget(signatureTarget);
500 }
501
502 if (!params.isMember(jss::tx_json))
503 return rpc::missingFieldError(jss::tx_json);
504
505 json::Value& txJson(params[jss::tx_json]);
506
507 // Check tx_json fields, but don't add any.
508 auto [txJsonResult, srcAddressID] = checkTxJsonFields(
509 txJson,
510 role,
511 verify,
512 validatedLedgerAge,
513 app.config(),
514 app.getFeeTrack(),
515 getAPIVersionNumber(params, app.config().betaRpcApi));
516
517 if (rpc::containsError(txJsonResult))
518 return std::move(txJsonResult);
519
520 // This test covers the case where we're offline so the sequence number
521 // cannot be determined locally. If we're offline then the caller must
522 // provide the sequence number.
523 if (!verify && !txJson.isMember(jss::Sequence))
524 return rpc::missingFieldError("tx_json.Sequence");
525
527 if (verify)
528 sle = app.getOpenLedger().current()->read(keylet::account(srcAddressID));
529
530 if (verify && !sle)
531 {
532 // If not offline and did not find account, error.
533 JLOG(j.debug()) << "transactionSign: Failed to find source account "
534 << "in current ledger: " << toBase58(srcAddressID);
535
537 }
538
539 if (signingArgs.editFields())
540 {
541 if (!txJson.isMember(jss::Sequence))
542 {
543 bool const hasTicketSeq = txJson.isMember(sfTicketSequence.jsonName);
544 if (!hasTicketSeq && !sle)
545 {
546 JLOG(j.debug()) << "transactionSign: Failed to find source account "
547 << "in current ledger: " << toBase58(srcAddressID);
548
550 }
551 txJson[jss::Sequence] = hasTicketSeq ? 0 : app.getTxQ().nextQueuableSeq(sle).value();
552 }
553
554 if (!txJson.isMember(jss::NetworkID))
555 {
556 auto const networkId = app.getNetworkIDService().getNetworkID();
557 if (networkId > 1024)
558 txJson[jss::NetworkID] = to_string(networkId);
559 }
560 }
561
562 {
563 json::Value err = checkFee(
564 params,
565 role,
566 verify && signingArgs.editFields(),
567 app.config(),
568 app.getFeeTrack(),
569 app.getTxQ(),
570 app);
571
572 if (rpc::containsError(err))
573 return err;
574 }
575
576 {
578 params, txJson, srcAddressID, role, app, verify && signingArgs.editFields());
579
580 if (rpc::containsError(err))
581 return err;
582 }
583
584 // If multisigning there should not be a single signature and vice versa.
585 if (signingArgs.isMultiSigning())
586 {
587 if (txJson.isMember(jss::TxnSignature))
589
590 // If multisigning then we need to return the public key.
591 signingArgs.setPublicKey(pk);
592 }
593 else if (signingArgs.isSingleSigning())
594 {
595 if (txJson.isMember(jss::Signers))
597 }
598
599 if (verify)
600 {
601 // sle validity is checked above
602 JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : "
603 << toBase58(srcAddressID);
604
605 // Don't do this test if multisigning or if the signature is going into
606 // an alternate field since the account and secret probably don't belong
607 // together in that case.
608 if (!signingArgs.isMultiSigning() && !signatureTarget)
609 {
610 // Make sure the account and secret belong together.
611 if (txJson.isMember(sfDelegate.jsonName))
612 {
613 // Delegated transaction
614 auto const delegateJson = txJson[sfDelegate.jsonName];
615 auto const ptrDelegatedAddressID = delegateJson.isString()
616 ? parseBase58<AccountID>(delegateJson.asString())
617 : std::nullopt;
618
619 if (!ptrDelegatedAddressID)
620 {
621 return rpc::makeError(
622 RpcSrcActMalformed, rpc::invalidFieldMessage("tx_json.Delegate"));
623 }
624
625 auto delegatedAddressID = *ptrDelegatedAddressID;
626 auto delegatedSle =
627 app.getOpenLedger().current()->read(keylet::account(delegatedAddressID));
628 if (!delegatedSle)
630
631 auto const err = acctMatchesPubKey(delegatedSle, delegatedAddressID, pk);
632
633 if (err != RpcSuccess)
634 return rpcError(err);
635 }
636 else
637 {
638 auto const err = acctMatchesPubKey(sle, srcAddressID, pk);
639
640 if (err != RpcSuccess)
641 return rpcError(err);
642 }
643 }
644 }
645
646 STParsedJSONObject parsed(std::string(jss::tx_json), txJson);
647 if (!parsed.object.has_value())
648 {
649 json::Value err;
650 err[jss::error] = parsed.error[jss::error];
651 err[jss::error_code] = parsed.error[jss::error_code];
652 err[jss::error_message] = parsed.error[jss::error_message];
653 return err;
654 }
655
657 try
658 {
659 // If we're generating a multi-signature the SigningPubKey must be
660 // empty, otherwise it must be the master account's public key.
661 STObject* sigObject = &*parsed.object;
662 if (signatureTarget)
663 {
664 // If the target object doesn't exist, make one.
665 if (!parsed.object->isFieldPresent(*signatureTarget))
666 {
667 parsed.object->setFieldObject(
668 *signatureTarget, STObject{*signatureTemplate, *signatureTarget});
669 }
670 sigObject = &parsed.object->peekFieldObject(*signatureTarget);
671 }
672 sigObject->setFieldVL(
673 sfSigningPubKey, signingArgs.isMultiSigning() ? Slice(nullptr, 0) : pk.slice());
674
675 stTx = std::make_shared<STTx>(std::move(parsed.object.value()));
676 }
677 catch (STObject::FieldErr const& err)
678 {
679 return rpc::makeError(RpcInvalidParams, err.what());
680 }
681 catch (std::exception&)
682 {
683 return rpc::makeError(
684 RpcInternal, "Exception occurred constructing serialized transaction");
685 }
686
687 std::string reason;
688 if (!passesLocalChecks(*stTx, reason))
689 return rpc::makeError(RpcInvalidParams, reason);
690
691 // If multisign then return multiSignature, else set TxnSignature field.
692 if (signingArgs.isMultiSigning())
693 {
695 *stTx, signingArgs.getSigner(), signingPrefix(*signatureRoleOpt, true, rules));
696
697 auto multisig = xrpl::sign(pk, sk, s.slice());
698
699 signingArgs.moveMultiSignature(std::move(multisig));
700 }
701 else if (signingArgs.isSingleSigning())
702 {
703 stTx->sign(pk, sk, *signatureRoleOpt, rules);
704 }
705
706 return TransactionPreProcessResult{std::move(stTx)};
707}
708
711 std::shared_ptr<STTx const> const& stTx,
712 Rules const& rules,
713 Application& app)
714{
716
717 // Turn the passed in STTx into a Transaction.
718 Transaction::pointer tpTrans;
719 {
720 std::string reason;
721 tpTrans = std::make_shared<Transaction>(stTx, reason, app);
722 if (tpTrans->getStatus() != TransStatus::NEW)
723 {
724 ret.first = rpc::makeError(RpcInternal, "Unable to construct transaction: " + reason);
725 return ret;
726 }
727 }
728 try
729 {
730 // Make sure the Transaction we just built is legit by serializing it
731 // and then de-serializing it. If the result isn't equivalent
732 // to the initial transaction then there's something wrong with the
733 // passed-in STTx.
734 {
735 Serializer s;
736 tpTrans->getSTransaction()->add(s);
737 Blob const transBlob = s.getData();
738 SerialIter sit{makeSlice(transBlob)};
739
740 // Check the signature if that's called for.
741 auto sttxNew = std::make_shared<STTx const>(sit);
742 if (!app.checkSigs())
743 {
745 app.getHashRouter(), sttxNew->getTransactionID(), Validity::SigGoodOnly);
746 }
747 if (checkValidity(app.getHashRouter(), *sttxNew, rules).first != Validity::Valid)
748 {
749 ret.first = rpc::makeError(RpcInternal, "Invalid signature.");
750 return ret;
751 }
752
753 std::string reason;
754 auto tpTransNew = std::make_shared<Transaction>(sttxNew, reason, app);
755
756 if (tpTransNew)
757 {
758 if (!tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
759 {
760 tpTransNew.reset();
761 }
762 tpTrans = std::move(tpTransNew);
763 }
764 }
765 }
766 catch (std::exception&)
767 {
768 // Assume that any exceptions are related to transaction sterilization.
769 tpTrans.reset();
770 }
771
772 if (!tpTrans)
773 {
774 ret.first = rpc::makeError(RpcInternal, "Unable to sterilize transaction.");
775 return ret;
776 }
777 ret.second = std::move(tpTrans);
778 return ret;
779}
780
781static json::Value
783{
784 json::Value jvResult;
785 try
786 {
787 if (apiVersion > 1)
788 {
789 jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::Values::DisableApiPriorV2);
790 jvResult[jss::hash] = to_string(tpTrans->getID());
791 }
792 else
793 {
794 jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::Values::None);
795 }
796
798 jvResult[jss::tx_json], tpTrans->getSTransaction()->getTxnType(), apiVersion);
799
800 jvResult[jss::tx_blob] = strHex(tpTrans->getSTransaction()->getSerializer().peekData());
801
802 if (temUNCERTAIN != tpTrans->getResult())
803 {
804 std::string sToken;
805 std::string sHuman;
806
807 transResultInfo(tpTrans->getResult(), sToken, sHuman);
808
809 jvResult[jss::engine_result] = sToken;
810 jvResult[jss::engine_result_code] = tpTrans->getResult();
811 jvResult[jss::engine_result_message] = sHuman;
812 }
813 }
814 catch (std::exception&)
815 {
816 jvResult = rpc::makeError(RpcInternal, "Exception occurred during JSON handling.");
817 }
818 return jvResult;
819}
820
821} // namespace detail
822
823//------------------------------------------------------------------------------
824
825[[nodiscard]] static XRPAmount
826getTxFee(Application const& app, Config const& config, json::Value tx)
827{
828 auto const& ledger = app.getOpenLedger().current();
829 // autofilling only needed in this function so that the `STParsedJSONObject`
830 // parsing works properly it should not be modifying the actual `tx` object
831 if (!tx.isMember(jss::Fee))
832 {
833 tx[jss::Fee] = "0";
834 }
835
836 if (!tx.isMember(jss::Sequence))
837 {
838 tx[jss::Sequence] = "0";
839 }
840
841 if (!tx.isMember(jss::SigningPubKey))
842 {
843 tx[jss::SigningPubKey] = "";
844 }
845
846 if (!tx.isMember(jss::TxnSignature))
847 {
848 tx[jss::TxnSignature] = "";
849 }
850
851 if (tx.isMember(jss::Signers))
852 {
853 if (!tx[jss::Signers].isArray())
854 return config.fees.referenceFee;
855
856 if (tx[jss::Signers].size() > STTx::kMaxMultiSigners)
857 return config.fees.referenceFee;
858
859 // check multi-signed signers
860 for (auto& signer : tx[jss::Signers])
861 {
862 if (!signer.isMember(jss::Signer) || !signer[jss::Signer].isObject())
863 return config.fees.referenceFee;
864 if (!signer[jss::Signer].isMember(jss::SigningPubKey))
865 {
866 // autofill SigningPubKey
867 signer[jss::Signer][jss::SigningPubKey] = "";
868 }
869 if (!signer[jss::Signer].isMember(jss::TxnSignature))
870 {
871 // autofill TxnSignature
872 signer[jss::Signer][jss::TxnSignature] = "";
873 }
874 }
875 }
876
877 STParsedJSONObject parsed(std::string(jss::tx_json), tx);
878 if (!parsed.object.has_value())
879 {
880 return config.fees.referenceFee;
881 }
882
883 try
884 {
885 STTx const& stTx = STTx(std::move(parsed.object.value()));
886 std::string reason;
887 if (!passesLocalChecks(stTx, reason))
888 return config.fees.referenceFee;
889
890 // This fee is only a suggestion returned to the caller, so fall back to
891 // the reference fee as the other failure paths in this function do.
892 return calculateBaseFee(*app.getOpenLedger().current(), stTx)
893 .value_or(config.fees.referenceFee);
894 }
895 catch (std::exception& e)
896 {
897 return config.fees.referenceFee;
898 }
899}
900
903 Role const role,
904 Config const& config,
905 LoadFeeTrack const& feeTrack,
906 TxQ const& txQ,
907 Application const& app,
908 json::Value const& tx,
909 int mult,
910 int div)
911{
912 XRPAmount const feeDefault = getTxFee(app, config, tx);
913
914 auto ledger = app.getOpenLedger().current();
915 // Administrative and identified endpoints are exempt from local fees.
916 XRPAmount const loadFee = scaleFeeLoad(feeDefault, feeTrack, ledger->fees(), isUnlimited(role));
917 XRPAmount fee = loadFee;
918 {
919 auto const metrics = txQ.getMetrics(*ledger);
920 auto const baseFee = ledger->fees().base;
921 auto escalatedFee = toDrops(metrics.openLedgerFeeLevel - FeeLevel64(1), baseFee) + 1;
922 fee = std::max(fee, escalatedFee);
923 }
924
925 auto const limit = mulDiv(feeDefault, mult, div);
926 if (!limit)
928
929 if (fee > *limit)
930 {
932 ss << "Fee of " << fee << " exceeds the requested tx limit of " << *limit;
933 return rpc::makeError(RpcHighFee, ss.str());
934 }
935
936 return fee.jsonClipped();
937}
938
941 json::Value& request,
942 Role const role,
943 bool doAutoFill,
944 Config const& config,
945 LoadFeeTrack const& feeTrack,
946 TxQ const& txQ,
947 Application const& app)
948{
949 json::Value& tx(request[jss::tx_json]);
950 if (tx.isMember(jss::Fee))
951 return json::Value();
952
953 if (!doAutoFill)
954 return rpc::missingFieldError("tx_json.Fee");
955
958 if (request.isMember(jss::fee_mult_max))
959 {
960 if (request[jss::fee_mult_max].isInt())
961 {
962 mult = request[jss::fee_mult_max].asInt();
963 if (mult < 0)
964 {
965 return rpc::makeError(
967 rpc::expectedFieldMessage(jss::fee_mult_max, "a positive integer"));
968 }
969 }
970 else
971 {
972 return rpc::makeError(
973 RpcHighFee, rpc::expectedFieldMessage(jss::fee_mult_max, "a positive integer"));
974 }
975 }
976 if (request.isMember(jss::fee_div_max))
977 {
978 if (request[jss::fee_div_max].isInt())
979 {
980 div = request[jss::fee_div_max].asInt();
981 if (div <= 0)
982 {
983 return rpc::makeError(
985 rpc::expectedFieldMessage(jss::fee_div_max, "a positive integer"));
986 }
987 }
988 else
989 {
990 return rpc::makeError(
991 RpcHighFee, rpc::expectedFieldMessage(jss::fee_div_max, "a positive integer"));
992 }
993 }
994
995 auto feeOrError = getCurrentNetworkFee(role, config, feeTrack, txQ, app, tx, mult, div);
996 if (feeOrError.isMember(jss::error))
997 return feeOrError;
998 tx[jss::Fee] = std::move(feeOrError);
999 return json::Value();
1000}
1001
1002//------------------------------------------------------------------------------
1003
1009 json::Value jvRequest,
1010 unsigned apiVersion,
1011 NetworkOPs::FailHard failType,
1012 Role role,
1013 std::chrono::seconds validatedLedgerAge,
1014 Application& app)
1015{
1016 using namespace detail;
1017
1018 // Sign and verify against the same ruleset: a ledger close in between
1019 // could change the signing prefix of an alternate signature field.
1021 auto j = app.getJournal("RPCHandler");
1022 JLOG(j.debug()) << "transactionSign: " << jvRequest;
1023
1024 // Add and amend fields based on the transaction type.
1025 SigningForParams signForParams;
1026 TransactionPreProcessResult const preprocResult = transactionPreProcessImpl(
1027 jvRequest, role, signForParams, validatedLedgerAge, app, ledger->rules());
1028
1029 if (!preprocResult.second)
1030 return preprocResult.first;
1031
1032 // Make sure the STTx makes a legitimate Transaction.
1034 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
1035
1036 if (!txn.second)
1037 return txn.first;
1038
1039 return transactionFormatResultImpl(txn.second, apiVersion);
1040}
1041
1047 json::Value jvRequest,
1048 unsigned apiVersion,
1049 NetworkOPs::FailHard failType,
1050 Role role,
1051 std::chrono::seconds validatedLedgerAge,
1052 Application& app,
1053 ProcessTransactionFn const& processTransaction)
1054{
1055 using namespace detail;
1056
1057 auto const& ledger = app.getOpenLedger().current();
1058 auto j = app.getJournal("RPCHandler");
1059 JLOG(j.debug()) << "transactionSubmit: " << jvRequest;
1060
1061 // Add and amend fields based on the transaction type.
1062 SigningForParams signForParams;
1063 TransactionPreProcessResult const preprocResult = transactionPreProcessImpl(
1064 jvRequest, role, signForParams, validatedLedgerAge, app, ledger->rules());
1065
1066 if (!preprocResult.second)
1067 return preprocResult.first;
1068
1069 // Make sure the STTx makes a legitimate Transaction.
1071 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
1072
1073 if (!txn.second)
1074 return txn.first;
1075
1076 // Finally, submit the transaction.
1077 try
1078 {
1079 // FIXME: For performance, should use async interface
1080 processTransaction(txn.second, isUnlimited(role), true, failType);
1081 }
1082 catch (std::exception&)
1083 {
1084 return rpc::makeError(RpcInternal, "Exception occurred during transaction submission.");
1085 }
1086
1087 return transactionFormatResultImpl(txn.second, apiVersion);
1088}
1089
1090namespace detail {
1091// There are a some field checks shared by transactionSignFor
1092// and transactionSubmitMultiSigned. Gather them together here.
1093static json::Value
1095{
1096 if (!jvRequest.isMember(jss::tx_json))
1097 return rpc::missingFieldError(jss::tx_json);
1098
1099 json::Value const& txJson(jvRequest[jss::tx_json]);
1100
1101 if (!txJson.isObject())
1102 return rpc::invalidFieldMessage(jss::tx_json);
1103
1104 // There are a couple of additional fields we need to check before
1105 // we serialize. If we serialize first then we generate less useful
1106 // error messages.
1107 if (!txJson.isMember(jss::Sequence))
1108 return rpc::missingFieldError("tx_json.Sequence");
1109
1110 if (!txJson.isMember(sfSigningPubKey.getJsonName()))
1111 return rpc::missingFieldError("tx_json.SigningPubKey");
1112
1113 // Multi-signing into a signature_target object field is fine,
1114 // because it means the signature is not for the transaction
1115 // Account.
1116 if (!jvRequest.isMember(jss::signature_target) &&
1117 !txJson[sfSigningPubKey.getJsonName()].asString().empty())
1118 {
1119 return rpc::makeError(
1120 RpcInvalidParams, "When multi-signing 'tx_json.SigningPubKey' must be empty.");
1121 }
1122
1123 return json::Value();
1124}
1125
1126// Sort and validate an stSigners array.
1127//
1128// Returns a null json::Value if there are no errors.
1129static json::Value
1130sortAndValidateSigners(STArray& signers, AccountID const& signingForID)
1131{
1132 if (signers.empty())
1133 return rpc::makeParamError("Signers array may not be empty.");
1134
1135 // Signers must be sorted by Account.
1136 std::ranges::sort(signers, [](STObject const& a, STObject const& b) {
1137 return (a[sfAccount] < b[sfAccount]);
1138 });
1139
1140 // Signers may not contain any duplicates.
1141 auto const dupIter = std::ranges::adjacent_find(
1142 signers,
1143 [](STObject const& a, STObject const& b) { return (a[sfAccount] == b[sfAccount]); });
1144
1145 if (dupIter != signers.end())
1146 {
1148 err << "Duplicate Signers:Signer:Account entries (" << toBase58((*dupIter)[sfAccount])
1149 << ") are not allowed.";
1150 return rpc::makeParamError(err.str());
1151 }
1152
1153 // An account may not sign for itself.
1154 if (signers.end() != std::ranges::find_if(signers, [&signingForID](STObject const& elem) {
1155 return elem[sfAccount] == signingForID;
1156 }))
1157 {
1159 err << "A Signer may not be the transaction's Account (" << toBase58(signingForID) << ").";
1160 return rpc::makeParamError(err.str());
1161 }
1162 return {};
1163}
1164
1165} // namespace detail
1166
1172 json::Value jvRequest,
1173 unsigned apiVersion,
1174 NetworkOPs::FailHard failType,
1175 Role role,
1176 std::chrono::seconds validatedLedgerAge,
1177 Application& app)
1178{
1179 auto const& ledger = app.getOpenLedger().current();
1180 auto j = app.getJournal("RPCHandler");
1181 JLOG(j.debug()) << "transactionSignFor: " << jvRequest;
1182
1183 // Verify presence of the signer's account field.
1184 char const accountField[] = "account";
1185
1186 if (!jvRequest.isMember(accountField))
1187 return rpc::missingFieldError(accountField);
1188
1189 // Turn the signer's account into an AccountID for multi-sign.
1190 auto const signerAccountID = parseBase58<AccountID>(jvRequest[accountField].asString());
1191 if (!signerAccountID)
1192 {
1194 }
1195
1196 if (!jvRequest.isMember(jss::tx_json))
1197 return rpc::missingFieldError(jss::tx_json);
1198
1199 {
1200 json::Value& txJson(jvRequest[jss::tx_json]);
1201
1202 if (!txJson.isObject())
1203 return rpc::objectFieldError(jss::tx_json);
1204
1205 if (auto checkResult =
1207 !checkResult)
1208 {
1209 return std::move(checkResult).error();
1210 }
1211
1212 // If the tx_json.SigningPubKey field is missing, insert an empty one,
1213 // in order for the `checkMultiSignFields` to not return an error
1214 // for non-multisign transactions.
1215 if (!txJson.isMember(sfSigningPubKey.getJsonName()))
1216 txJson[sfSigningPubKey.getJsonName()] = "";
1217 }
1218
1219 // When multi-signing, the "Sequence" and "SigningPubKey" fields must
1220 // be passed in by the caller.
1221 using namespace detail;
1222 {
1223 json::Value err = checkMultiSignFields(jvRequest);
1224 if (rpc::containsError(err))
1225 return err;
1226 }
1227
1228 // Add and amend fields based on the transaction type.
1229 SigningForParams signForParams(*signerAccountID);
1230
1231 TransactionPreProcessResult const preprocResult = transactionPreProcessImpl(
1232 jvRequest, role, signForParams, validatedLedgerAge, app, ledger->rules());
1233
1234 if (!preprocResult.second)
1235 return preprocResult.first;
1236
1237 XRPL_ASSERT(
1238 signForParams.validMultiSign(), "xrpl::rpc::transactionSignFor : valid multi-signature");
1239
1240 {
1241 SLE::const_pointer const accountState = ledger->read(keylet::account(*signerAccountID));
1242 // Make sure the account and secret belong together.
1243 auto const err =
1244 acctMatchesPubKey(accountState, *signerAccountID, signForParams.getPublicKey());
1245
1246 if (err != RpcSuccess)
1247 return rpcError(err);
1248 }
1249
1250 // Inject the newly generated signature into tx_json.Signers.
1251 auto& sttx = preprocResult.second;
1252 {
1253 // Make the signer object that we'll inject.
1254 STObject signer = STObject::makeInnerObject(sfSigner);
1255 signer[sfAccount] = *signerAccountID;
1256 signer.setFieldVL(sfTxnSignature, signForParams.getSignature());
1257 signer.setFieldVL(sfSigningPubKey, signForParams.getPublicKey().slice());
1258
1259 STObject& sigTarget = [&]() -> STObject& {
1260 auto const target = signForParams.getSignatureTarget();
1261 if (target)
1262 return sttx->peekFieldObject(*target);
1263 return *sttx;
1264 }();
1265 // If there is not yet a Signers array, make one.
1266 if (!sigTarget.isFieldPresent(sfSigners))
1267 sigTarget.setFieldArray(sfSigners, {});
1268
1269 auto& signers = sigTarget.peekFieldArray(sfSigners);
1270 signers.emplaceBack(std::move(signer));
1271
1272 // The array must be sorted and validated.
1273 // For delegated transactions, the delegate account is
1274 // the one forbidden from appearing in its own Signers array.
1275 auto err = sortAndValidateSigners(signers, sttx->getInitiator());
1276 if (rpc::containsError(err))
1277 return err;
1278 }
1279
1280 // Make sure the STTx makes a legitimate Transaction.
1282 transactionConstructImpl(sttx, ledger->rules(), app);
1283
1284 if (!txn.second)
1285 return txn.first;
1286
1287 return transactionFormatResultImpl(txn.second, apiVersion);
1288}
1289
1295 json::Value jvRequest,
1296 unsigned apiVersion,
1297 NetworkOPs::FailHard failType,
1298 Role role,
1299 std::chrono::seconds validatedLedgerAge,
1300 Application& app,
1301 ProcessTransactionFn const& processTransaction)
1302{
1303 auto const& ledger = app.getOpenLedger().current();
1304 auto j = app.getJournal("RPCHandler");
1305 JLOG(j.debug()) << "transactionSubmitMultiSigned: " << jvRequest;
1306
1307 // When multi-signing, the "Sequence" and "SigningPubKey" fields must
1308 // be passed in by the caller.
1309 using namespace detail;
1310 {
1311 json::Value err = checkMultiSignFields(jvRequest);
1312 if (rpc::containsError(err))
1313 return err;
1314 }
1315
1316 json::Value& txJson(jvRequest["tx_json"]);
1317
1318 auto [txJsonResult, srcAddressID] = checkTxJsonFields(
1319 txJson,
1320 role,
1321 true,
1322 validatedLedgerAge,
1323 app.config(),
1324 app.getFeeTrack(),
1325 getAPIVersionNumber(jvRequest, app.config().betaRpcApi));
1326
1327 if (rpc::containsError(txJsonResult))
1328 return std::move(txJsonResult);
1329
1330 SLE::const_pointer const sle = ledger->read(keylet::account(srcAddressID));
1331
1332 if (!sle)
1333 {
1334 // If did not find account, error.
1335 JLOG(j.debug()) << "transactionSubmitMultiSigned: Failed to find source account "
1336 << "in current ledger: " << toBase58(srcAddressID);
1337
1339 }
1340
1341 {
1342 json::Value err =
1343 checkFee(jvRequest, role, false, app.config(), app.getFeeTrack(), app.getTxQ(), app);
1344
1345 if (rpc::containsError(err))
1346 return err;
1347
1348 err = checkPayment(jvRequest, txJson, srcAddressID, role, app, false);
1349
1350 if (rpc::containsError(err))
1351 return err;
1352 }
1353
1354 // Grind through the JSON in tx_json to produce a STTx.
1356 {
1357 STParsedJSONObject parsedTxJson("tx_json", txJson);
1358 if (!parsedTxJson.object)
1359 {
1360 json::Value jvResult;
1361 jvResult["error"] = parsedTxJson.error["error"];
1362 jvResult["error_code"] = parsedTxJson.error["error_code"];
1363 jvResult["error_message"] = parsedTxJson.error["error_message"];
1364 return jvResult;
1365 }
1366 try
1367 {
1368 stTx = std::make_shared<STTx>(std::move(parsedTxJson.object.value()));
1369 }
1370 catch (STObject::FieldErr const& err)
1371 {
1372 return rpc::makeError(RpcInvalidParams, err.what());
1373 }
1374 catch (std::exception& ex)
1375 {
1376 std::string const reason(ex.what());
1377 return rpc::makeError(
1378 RpcInternal, "Exception while serializing transaction: " + reason);
1379 }
1380 std::string reason;
1381 if (!passesLocalChecks(*stTx, reason))
1382 return rpc::makeError(RpcInvalidParams, reason);
1383 }
1384
1385 // Validate the fields in the serialized transaction.
1386 {
1387 // We now have the transaction text serialized and in the right format.
1388 // Verify the values of select fields.
1389 //
1390 // The SigningPubKey must be present but empty.
1391 if (!stTx->getFieldVL(sfSigningPubKey).empty())
1392 {
1394 err << "Invalid " << sfSigningPubKey.fieldName
1395 << " field. Field must be empty when multi-signing.";
1396 return rpc::makeError(RpcInvalidParams, err.str());
1397 }
1398
1399 // There may not be a TxnSignature field.
1400 if (stTx->isFieldPresent(sfTxnSignature))
1402
1403 // The Fee field must be in XRP and greater than zero.
1404 auto const fee = stTx->getFieldAmount(sfFee);
1405
1406 if (!isLegalNet(fee))
1407 {
1409 err << "Invalid " << sfFee.fieldName << " field. Fees must be specified in XRP.";
1410 return rpc::makeError(RpcInvalidParams, err.str());
1411 }
1412 if (fee <= STAmount{0})
1413 {
1415 err << "Invalid " << sfFee.fieldName << " field. Fees must be greater than zero.";
1416 return rpc::makeError(RpcInvalidParams, err.str());
1417 }
1418 }
1419
1420 // Verify that the Signers field is present.
1421 if (!stTx->isFieldPresent(sfSigners))
1422 return rpc::missingFieldError("tx_json.Signers");
1423
1424 // If the Signers field is present the SField guarantees it to be an array.
1425 // Get a reference to the Signers array so we can verify and sort it.
1426 auto& signers = stTx->peekFieldArray(sfSigners);
1427
1428 if (signers.empty())
1429 return rpc::makeParamError("tx_json.Signers array may not be empty.");
1430
1431 // The Signers array may only contain Signer objects.
1432 if (std::ranges::find_if_not(signers, [](STObject const& obj) {
1433 return (
1434 // A Signer object always contains these fields and no
1435 // others.
1436 obj.isFieldPresent(sfAccount) && obj.isFieldPresent(sfSigningPubKey) &&
1437 obj.isFieldPresent(sfTxnSignature) && obj.getCount() == 3);
1438 }) != signers.end())
1439 {
1440 return rpc::makeParamError("Signers array may only contain Signer entries.");
1441 }
1442
1443 // The array must be sorted and validated.
1444 // For delegated transactions, getInitiator() returns sfDelegate,
1445 // that account is the one forbidden from appearing in its own Signers array.
1446 auto err = sortAndValidateSigners(signers, stTx->getInitiator());
1447 if (rpc::containsError(err))
1448 return err;
1449
1450 // Make sure the SerializedTransaction makes a legitimate Transaction.
1452 transactionConstructImpl(stTx, ledger->rules(), app);
1453
1454 if (!txn.second)
1455 return txn.first;
1456
1457 // Finally, submit the transaction.
1458 try
1459 {
1460 // FIXME: For performance, should use async interface
1461 processTransaction(txn.second, isUnlimited(role), true, failType);
1462 }
1463 catch (std::exception&)
1464 {
1465 return rpc::makeError(RpcInternal, "Exception occurred during transaction submission.");
1466 }
1467
1468 return transactionFormatResultImpl(txn.second, apiVersion);
1469}
1470
1471} // namespace xrpl::rpc
T adjacent_find(T... args)
Represents a JSON value.
Definition json_value.h:117
Value removeMember(char const *key)
Remove and return the named member.
bool isObject() const
bool isString() const
const_iterator end() const
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.
Int asInt() const
virtual Config & config()=0
virtual bool checkSigs() const =0
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:117
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:525
Like std::vector<char> but better.
Definition Buffer.h:19
bool standalone() const
SOTemplate const * findSOTemplateBySField(SField const &sField) const
static InnerObjectFormats const & getInstance()
A currency issued by an account.
Definition Issue.h:18
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.
Definition Pathfinder.h:37
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.
A public key.
Definition PublicKey.h:53
Slice slice() const noexcept
Definition PublicKey.h:115
Rules controlling protocol behavior.
Definition Rules.h:40
static SField const & getField(int fieldCode)
Definition SField.cpp:108
constexpr bool holds() const noexcept
Definition STAmount.h:478
constexpr TIss const & get() const
bool native() const noexcept
Definition STAmount.h:471
Asset const & asset() const
Definition STAmount.h:496
AccountID const & getIssuer() const
Definition STAmount.h:516
std::shared_ptr< STLedgerEntry const > const_pointer
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:791
STArray & peekFieldArray(SField const &field)
Definition STObject.cpp:481
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:833
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
int getCount() const
Definition STObject.h:1019
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:79
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
bool empty() const
Definition STPathSet.h:630
static constexpr std::size_t kMaxMultiSigners
Definition STTx.h:49
A secret key.
Definition SecretKey.h:24
constexpr std::uint32_t value() const
Definition SeqProxy.h:80
Slice slice() const noexcept
Definition Serializer.h:141
Blob getData() const
Definition Serializer.h:278
virtual TxQ & getTxQ()=0
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.
Definition Slice.h:28
std::shared_ptr< Transaction > pointer
Definition Transaction.h:57
Transaction Queue.
Definition TxQ.h:59
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1786
SeqProxy nextQueuableSeq(SLE::ConstRef sleAccount) const
Return the next sequence that would go in the TxQ for an account.
Definition TxQ.cpp:1619
json::Value jsonClipped() const
Definition XRPAmount.h:210
std::optional< std::reference_wrapper< SField const > > const & getSignatureTarget() const
SigningForParams(AccountID const &multiSigningAcctID)
PublicKey const & getPublicKey() const
SigningForParams(SigningForParams const &rhs)=delete
void moveMultiSignature(Buffer &&multiSignature)
std::optional< std::reference_wrapper< SField const > > signatureTarget_
void setSignatureTarget(std::optional< std::reference_wrapper< SField const > > const &field)
AccountID const *const multiSigningAcctID_
std::optional< PublicKey > multiSignPublicKey_
void setPublicKey(PublicKey const &multiSignPublicKey)
AccountID const & getSigner() const
T empty(T... args)
T find_if(T... args)
T make_shared(T... args)
T max(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
STL namespace.
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
static std::pair< json::Value, AccountID > checkTxJsonFields(json::Value const &txJson, Role const role, bool const verify, std::chrono::seconds validatedLedgerAge, Config const &config, LoadFeeTrack const &feeTrack, unsigned apiVersion)
static ErrorCodeI acctMatchesPubKey(SLE::const_pointer accountState, AccountID const &accountID, PublicKey const &publicKey)
static json::Value transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion)
static std::pair< json::Value, Transaction::pointer > transactionConstructImpl(std::shared_ptr< STTx const > const &stTx, Rules const &rules, Application &app)
static std::expected< void, json::Value > checkNetworkID(json::Value const &txJson, uint32_t appNetworkId)
static json::Value checkMultiSignFields(json::Value const &jvRequest)
static TransactionPreProcessResult transactionPreProcessImpl(json::Value &params, Role role, SigningForParams &signingArgs, std::chrono::seconds validatedLedgerAge, Application &app, Rules const &rules)
static json::Value checkPayment(json::Value const &params, json::Value &txJson, AccountID const &srcAddressID, Role const role, Application &app, bool doPath)
static json::Value sortAndValidateSigners(STArray &signers, AccountID const &signingForID)
static constexpr int kDefaultAutoFillFeeMultiplier
static constexpr int kDefaultAutoFillFeeDivisor
API version numbers used in later API versions.
Definition ApiVersion.h:36
bool containsError(json::Value const &json)
Returns true if the json contains an rpc error specification.
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 transactionSubmit(json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a json::ValueType::Object.
std::function< void( std::shared_ptr< Transaction > &transaction, bool bUnlimited, bool bLocal, NetworkOPs::FailHard failType)> ProcessTransactionFn
void insertDeliverMax(json::Value &txJson, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
static XRPAmount getTxFee(Application const &app, Config const &config, json::Value tx)
std::string invalidFieldMessage(std::string const &name)
Definition ErrorCodes.h:273
json::Value makeParamError(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition ErrorCodes.h:231
unsigned int getAPIVersionNumber(json::Value const &jv, bool betaEnabled)
Retrieve the api version number from the json value.
Definition ApiVersion.h:100
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:285
std::string expectedFieldMessage(std::string const &name, std::string const &type)
Definition ErrorCodes.h:297
json::Value transactionSignFor(json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a json::ValueType::Object.
std::string missingFieldMessage(std::string const &name)
Definition ErrorCodes.h:237
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::ValueType::Object.
json::Value transactionSign(json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a json::ValueType::Object.
json::Value missingFieldError(std::string const &name)
Definition ErrorCodes.h:243
json::Value makeError(ErrorCodeI code)
Returns a new json object that reflects the error code.
json::Value objectFieldError(std::string const &name)
Definition ErrorCodes.h:261
std::optional< std::pair< PublicKey, SecretKey > > keypairForSignature(json::Value const &params, json::Value &error, unsigned int apiVersion)
Generates a keypair for signature from RPC parameters.
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.
ErrorCodeI
Definition ErrorCodes.h:23
@ RpcBadSecret
Definition ErrorCodes.h:81
@ RpcInternal
Definition ErrorCodes.h:113
@ RpcDelegateActNotFound
Definition ErrorCodes.h:106
@ RpcSrcActMalformed
Definition ErrorCodes.h:103
@ RpcAlreadySingleSig
Definition ErrorCodes.h:75
@ RpcSigningMalformed
Definition ErrorCodes.h:101
@ RpcSuccess
Definition ErrorCodes.h:27
@ RpcNotSynced
Definition ErrorCodes.h:50
@ RpcHighFee
Definition ErrorCodes.h:41
@ RpcMasterDisabled
Definition ErrorCodes.h:57
@ RpcDomainMalformed
Definition ErrorCodes.h:141
@ RpcSrcActMissing
Definition ErrorCodes.h:104
@ RpcInvalidParams
Definition ErrorCodes.h:67
@ RpcTooBusy
Definition ErrorCodes.h:39
@ RpcAlreadyMultisig
Definition ErrorCodes.h:74
@ RpcNoCurrent
Definition ErrorCodes.h:48
@ RpcSrcActNotFound
Definition ErrorCodes.h:105
std::optional< SignatureRole > signatureRole(SField const &sigField)
The role that signs into the given field.
std::optional< std::uint64_t > mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
Return value*mul/div accurately.
std::expected< XRPAmount, TER > calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
@ Valid
Signature and local checks are good / passed.
Definition apply.h:36
@ SigGoodOnly
Signature is good, but local checks fail.
Definition apply.h:32
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
HashPrefix signingPrefix(SignatureRole role, bool multiSigning, Rules const &rules)
The hash prefix that binds a transaction signature to the role that made it.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules)
Checks transaction signature and local checks.
Definition apply.cpp:62
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
bool isLegalNet(STAmount const &value)
Definition STAmount.h:616
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition TxQ.h:1004
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:242
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
bool passesLocalChecks(STTx const &tx, std::string &)
Definition STTx.cpp:850
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
void forceValidity(HashRouter &router, UInt256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:141
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
Role
Indicates the level of administrative permission to grant.
Definition Role.h:27
BaseUInt< 256 > UInt256
Definition base_uint.h:580
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
@ Transaction
The transaction's own signature, in sfTxnSignature or sfSigners.
Definition Sign.h:27
FeeLevel< std::uint64_t > FeeLevel64
Definition Units.h:443
AccountID calcAccountID(PublicKey const &pk)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
@ temUNCERTAIN
Definition TER.h:111
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.
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:115
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
Serializer buildMultiSigningData(STObject const &obj, AccountID const &signingID, HashPrefix prefix)
Return a Serializer suitable for computing a multisigning TxnSignature.
T reset(T... args)
T sort(T... args)
T str(T... args)
XRPAmount referenceFee
The cost of a reference transaction in drops.
TransactionPreProcessResult & operator=(TransactionPreProcessResult const &)=delete
TransactionPreProcessResult(std::shared_ptr< STTx > &&st)
TransactionPreProcessResult(TransactionPreProcessResult const &)=delete
TransactionPreProcessResult & operator=(TransactionPreProcessResult &&)=delete
TransactionPreProcessResult(TransactionPreProcessResult &&rhs)=default
T unexpected(T... args)
T what(T... args)