rippled
Loading...
Searching...
No Matches
TransactionSign.cpp
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/app/paths/Pathfinder.h>
7#include <xrpld/app/tx/apply.h> // Validity::Valid
8#include <xrpld/app/tx/applySteps.h>
9#include <xrpld/rpc/detail/LegacyPathFind.h>
10#include <xrpld/rpc/detail/RPCHelpers.h>
11#include <xrpld/rpc/detail/TransactionSign.h>
12
13#include <xrpl/basics/Log.h>
14#include <xrpl/basics/mulDiv.h>
15#include <xrpl/json/json_writer.h>
16#include <xrpl/protocol/ErrorCodes.h>
17#include <xrpl/protocol/InnerObjectFormats.h>
18#include <xrpl/protocol/RPCErr.h>
19#include <xrpl/protocol/STParsedJSON.h>
20#include <xrpl/protocol/Sign.h>
21#include <xrpl/protocol/TxFlags.h>
22
23#include <algorithm>
24#include <iterator>
25#include <optional>
26
27namespace xrpl {
28namespace RPC {
29namespace detail {
30
31// Used to pass extra parameters used when returning a
32// a SigningFor object.
34{
35private:
40
41public:
43 {
44 }
45
46 SigningForParams(SigningForParams const& rhs) = delete;
47
48 SigningForParams(AccountID const& multiSigningAcctID) : multiSigningAcctID_(&multiSigningAcctID)
49 {
50 }
51
52 bool
54 {
55 return multiSigningAcctID_ != nullptr;
56 }
57
58 bool
60 {
61 return !isMultiSigning();
62 }
63
64 // When multi-signing we should not edit the tx_json fields.
65 bool
66 editFields() const
67 {
68 return !isMultiSigning();
69 }
70
71 bool
76
77 // Don't call this method unless isMultiSigning() returns true.
78 AccountID const&
79 getSigner() const
80 {
82 LogicError("Accessing unknown SigningForParams::getSigner()");
83 return *multiSigningAcctID_;
84 }
85
86 PublicKey const&
88 {
90 LogicError("Accessing unknown SigningForParams::getPublicKey()");
91 return *multiSignPublicKey_;
92 }
93
94 Buffer const&
96 {
97 return multiSignature_;
98 }
99
102 {
103 return signatureTarget_;
104 }
105
106 void
107 setPublicKey(PublicKey const& multiSignPublicKey)
108 {
109 multiSignPublicKey_ = multiSignPublicKey;
110 }
111
112 void
117
118 void
119 moveMultiSignature(Buffer&& multiSignature)
120 {
121 multiSignature_ = std::move(multiSignature);
122 }
123};
124
125//------------------------------------------------------------------------------
126
127static error_code_i
128acctMatchesPubKey(std::shared_ptr<SLE const> accountState, AccountID const& accountID, PublicKey const& publicKey)
129{
130 auto const publicKeyAcctID = calcAccountID(publicKey);
131 bool const isMasterKey = publicKeyAcctID == accountID;
132
133 // If we can't get the accountRoot, but the accountIDs match, that's
134 // good enough.
135 if (!accountState)
136 {
137 if (isMasterKey)
138 return rpcSUCCESS;
139 return rpcBAD_SECRET;
140 }
141
142 // If we *can* get to the accountRoot, check for MASTER_DISABLED.
143 auto const& sle = *accountState;
144 if (isMasterKey)
145 {
146 if (sle.isFlag(lsfDisableMaster))
147 return rpcMASTER_DISABLED;
148 return rpcSUCCESS;
149 }
150
151 // The last gasp is that we have public Regular key.
152 if ((sle.isFieldPresent(sfRegularKey)) && (publicKeyAcctID == sle.getAccountID(sfRegularKey)))
153 {
154 return rpcSUCCESS;
155 }
156 return rpcBAD_SECRET;
157}
158
159static Json::Value
161 Json::Value const& params,
162 Json::Value& tx_json,
163 AccountID const& srcAddressID,
164 Role const role,
165 Application& app,
166 bool doPath)
167{
168 // Only path find for Payments.
169 if (tx_json[jss::TransactionType].asString() != jss::Payment)
170 return Json::Value();
171
172 // DeliverMax is an alias to Amount and we use Amount internally
173 if (tx_json.isMember(jss::DeliverMax))
174 {
175 if (tx_json.isMember(jss::Amount))
176 {
177 if (tx_json[jss::DeliverMax] != tx_json[jss::Amount])
178 return RPC::make_error(rpcINVALID_PARAMS, "Cannot specify differing 'Amount' and 'DeliverMax'");
179 }
180 else
181 tx_json[jss::Amount] = tx_json[jss::DeliverMax];
182
183 tx_json.removeMember(jss::DeliverMax);
184 }
185
186 if (!tx_json.isMember(jss::Amount))
187 return RPC::missing_field_error("tx_json.Amount");
188
189 STAmount amount;
190
191 if (!amountFromJsonNoThrow(amount, tx_json[jss::Amount]))
192 return RPC::invalid_field_error("tx_json.Amount");
193
194 if (!tx_json.isMember(jss::Destination))
195 return RPC::missing_field_error("tx_json.Destination");
196
197 auto const dstAccountID = parseBase58<AccountID>(tx_json[jss::Destination].asString());
198 if (!dstAccountID)
199 return RPC::invalid_field_error("tx_json.Destination");
200
201 if (params.isMember(jss::build_path) && ((doPath == false) || amount.holds<MPTIssue>()))
202 return RPC::make_error(rpcINVALID_PARAMS, "Field 'build_path' not allowed in this context.");
203
204 if (tx_json.isMember(jss::Paths) && params.isMember(jss::build_path))
205 return RPC::make_error(rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'build_path'");
206
208 if (tx_json.isMember(sfDomainID.jsonName))
209 {
210 uint256 num;
211 if (!tx_json[sfDomainID.jsonName].isString() || !num.parseHex(tx_json[sfDomainID.jsonName].asString()))
212 {
213 return RPC::make_error(rpcDOMAIN_MALFORMED, "Unable to parse 'DomainID'.");
214 }
215 else
216 {
217 domain = num;
218 }
219 }
220
221 if (!tx_json.isMember(jss::Paths) && params.isMember(jss::build_path))
222 {
223 STAmount sendMax;
224
225 if (tx_json.isMember(jss::SendMax))
226 {
227 if (!amountFromJsonNoThrow(sendMax, tx_json[jss::SendMax]))
228 return RPC::invalid_field_error("tx_json.SendMax");
229 }
230 else
231 {
232 // If no SendMax, default to Amount with sender as issuer.
233 sendMax = amount;
234 sendMax.setIssuer(srcAddressID);
235 }
236
237 if (sendMax.native() && amount.native())
238 return RPC::make_error(rpcINVALID_PARAMS, "Cannot build XRP to XRP paths.");
239
240 {
241 LegacyPathFind lpf(isUnlimited(role), app);
242 if (!lpf.isOk())
243 return rpcError(rpcTOO_BUSY);
244
245 STPathSet result;
246
247 if (auto ledger = app.openLedger().current())
248 {
249 Pathfinder pf(
250 std::make_shared<RippleLineCache>(ledger, app.journal("RippleLineCache")),
251 srcAddressID,
252 *dstAccountID,
253 sendMax.issue().currency,
254 sendMax.issue().account,
255 amount,
257 domain,
258 app);
259 if (pf.findPaths(app.config().PATH_SEARCH_OLD))
260 {
261 // 4 is the maximum paths
262 pf.computePathRanks(4);
263 STPath fullLiquidityPath;
264 STPathSet paths;
265 result = pf.getBestPaths(4, fullLiquidityPath, paths, sendMax.issue().account);
266 }
267 }
268
269 auto j = app.journal("RPCHandler");
270 JLOG(j.debug()) << "transactionSign: build_path: " << result.getJson(JsonOptions::none);
271
272 if (!result.empty())
273 tx_json[jss::Paths] = result.getJson(JsonOptions::none);
274 }
275 }
276 return Json::Value();
277}
278
279//------------------------------------------------------------------------------
280
281// Validate (but don't modify) the contents of the tx_json.
282//
283// Returns a pair<Json::Value, AccountID>. The Json::Value will contain error
284// information if there was an error. On success, the account ID is returned
285// and the Json::Value will be empty.
286//
287// This code does not check the "Sequence" field, since the expectations
288// for that field are particularly context sensitive.
291 Json::Value const& tx_json,
292 Role const role,
293 bool const verify,
294 std::chrono::seconds validatedLedgerAge,
295 Config const& config,
296 LoadFeeTrack const& feeTrack,
297 unsigned apiVersion)
298{
300
301 if (!tx_json.isObject())
302 {
303 ret.first = RPC::object_field_error(jss::tx_json);
304 return ret;
305 }
306
307 if (!tx_json.isMember(jss::TransactionType))
308 {
309 ret.first = RPC::missing_field_error("tx_json.TransactionType");
310 return ret;
311 }
312
313 if (!tx_json.isMember(jss::Account))
314 {
316 return ret;
317 }
318
319 auto const srcAddressID = parseBase58<AccountID>(tx_json[jss::Account].asString());
320
321 if (!srcAddressID)
322 {
324 return ret;
325 }
326
327 // Check for current ledger.
328 if (verify && !config.standalone() && (validatedLedgerAge > Tuning::maxValidatedLedgerAge))
329 {
330 if (apiVersion == 1)
332 else
334 return ret;
335 }
336
337 // Check for load.
338 if (feeTrack.isLoadedCluster() && !isUnlimited(role))
339 {
341 return ret;
342 }
343
344 // It's all good. Return the AccountID.
345 ret.second = *srcAddressID;
346 return ret;
347}
348
349//------------------------------------------------------------------------------
350
351// A move-only struct that makes it easy to return either a Json::Value or a
352// std::shared_ptr<STTx const> from transactionPreProcessImpl ().
375
376static transactionPreProcessResult
378 Json::Value& params,
379 Role role,
380 SigningForParams& signingArgs,
381 std::chrono::seconds validatedLedgerAge,
382 Application& app)
383{
384 auto j = app.journal("RPCHandler");
385
386 Json::Value jvResult;
388 if (!keyPair || contains_error(jvResult))
389 return jvResult;
390
391 PublicKey const& pk = keyPair->first;
392 SecretKey const& sk = keyPair->second;
393
394 bool const verify = !(params.isMember(jss::offline) && params[jss::offline].asBool());
395
396 auto const signatureTarget = [&params]() -> std::optional<std::reference_wrapper<SField const>> {
397 if (params.isMember(jss::signature_target))
398 return SField::getField(params[jss::signature_target].asString());
399 return std::nullopt;
400 }();
401
402 // Make sure the signature target field is valid, if specified, and save the
403 // template for use later
404 auto const signatureTemplate =
405 signatureTarget ? InnerObjectFormats::getInstance().findSOTemplateBySField(*signatureTarget) : nullptr;
406 if (signatureTarget)
407 {
408 if (!signatureTemplate)
409 { // Invalid target field
410 return RPC::make_error(rpcINVALID_PARAMS, signatureTarget->get().getName());
411 }
412 signingArgs.setSignatureTarget(signatureTarget);
413 }
414
415 if (!params.isMember(jss::tx_json))
416 return RPC::missing_field_error(jss::tx_json);
417
418 Json::Value& tx_json(params[jss::tx_json]);
419
420 // Check tx_json fields, but don't add any.
421 auto [txJsonResult, srcAddressID] = checkTxJsonFields(
422 tx_json,
423 role,
424 verify,
425 validatedLedgerAge,
426 app.config(),
427 app.getFeeTrack(),
429
430 if (RPC::contains_error(txJsonResult))
431 return std::move(txJsonResult);
432
433 // This test covers the case where we're offline so the sequence number
434 // cannot be determined locally. If we're offline then the caller must
435 // provide the sequence number.
436 if (!verify && !tx_json.isMember(jss::Sequence))
437 return RPC::missing_field_error("tx_json.Sequence");
438
440 if (verify)
441 sle = app.openLedger().current()->read(keylet::account(srcAddressID));
442
443 if (verify && !sle)
444 {
445 // If not offline and did not find account, error.
446 JLOG(j.debug()) << "transactionSign: Failed to find source account "
447 << "in current ledger: " << toBase58(srcAddressID);
448
450 }
451
452 if (signingArgs.editFields())
453 {
454 if (!tx_json.isMember(jss::Sequence))
455 {
456 bool const hasTicketSeq = tx_json.isMember(sfTicketSequence.jsonName);
457 if (!hasTicketSeq && !sle)
458 {
459 JLOG(j.debug()) << "transactionSign: Failed to find source account "
460 << "in current ledger: " << toBase58(srcAddressID);
461
463 }
464 tx_json[jss::Sequence] = hasTicketSeq ? 0 : app.getTxQ().nextQueuableSeq(sle).value();
465 }
466
467 if (!tx_json.isMember(jss::NetworkID))
468 {
469 auto const networkId = app.config().NETWORK_ID;
470 if (networkId > 1024)
471 tx_json[jss::NetworkID] = to_string(networkId);
472 }
473 }
474
475 {
476 Json::Value err = checkFee(
477 params, role, verify && signingArgs.editFields(), app.config(), app.getFeeTrack(), app.getTxQ(), app);
478
479 if (RPC::contains_error(err))
480 return err;
481 }
482
483 {
484 Json::Value err = checkPayment(params, tx_json, srcAddressID, role, app, verify && signingArgs.editFields());
485
486 if (RPC::contains_error(err))
487 return err;
488 }
489
490 // If multisigning there should not be a single signature and vice versa.
491 if (signingArgs.isMultiSigning())
492 {
493 if (tx_json.isMember(jss::TxnSignature))
495
496 // If multisigning then we need to return the public key.
497 signingArgs.setPublicKey(pk);
498 }
499 else if (signingArgs.isSingleSigning())
500 {
501 if (tx_json.isMember(jss::Signers))
503 }
504
505 if (verify)
506 {
507 if (!sle)
508 // XXX Ignore transactions for accounts not created.
510
511 JLOG(j.trace()) << "verify: " << toBase58(calcAccountID(pk)) << " : " << toBase58(srcAddressID);
512
513 // Don't do this test if multisigning or if the signature is going into
514 // an alternate field since the account and secret probably don't belong
515 // together in that case.
516 if (!signingArgs.isMultiSigning() && !signatureTarget)
517 {
518 // Make sure the account and secret belong together.
519 if (tx_json.isMember(sfDelegate.jsonName))
520 {
521 // Delegated transaction
522 auto const delegateJson = tx_json[sfDelegate.jsonName];
523 auto const ptrDelegatedAddressID =
524 delegateJson.isString() ? parseBase58<AccountID>(delegateJson.asString()) : std::nullopt;
525
526 if (!ptrDelegatedAddressID)
527 {
529 }
530
531 auto delegatedAddressID = *ptrDelegatedAddressID;
532 auto delegatedSle = app.openLedger().current()->read(keylet::account(delegatedAddressID));
533 if (!delegatedSle)
535
536 auto const err = acctMatchesPubKey(delegatedSle, delegatedAddressID, pk);
537
538 if (err != rpcSUCCESS)
539 return rpcError(err);
540 }
541 else
542 {
543 auto const err = acctMatchesPubKey(sle, srcAddressID, pk);
544
545 if (err != rpcSUCCESS)
546 return rpcError(err);
547 }
548 }
549 }
550
551 STParsedJSONObject parsed(std::string(jss::tx_json), tx_json);
552 if (!parsed.object.has_value())
553 {
554 Json::Value err;
555 err[jss::error] = parsed.error[jss::error];
556 err[jss::error_code] = parsed.error[jss::error_code];
557 err[jss::error_message] = parsed.error[jss::error_message];
558 return err;
559 }
560
562 try
563 {
564 // If we're generating a multi-signature the SigningPubKey must be
565 // empty, otherwise it must be the master account's public key.
566 STObject* sigObject = &*parsed.object;
567 if (signatureTarget)
568 {
569 // If the target object doesn't exist, make one.
570 if (!parsed.object->isFieldPresent(*signatureTarget))
571 parsed.object->setFieldObject(*signatureTarget, STObject{*signatureTemplate, *signatureTarget});
572 sigObject = &parsed.object->peekFieldObject(*signatureTarget);
573 }
574 sigObject->setFieldVL(sfSigningPubKey, signingArgs.isMultiSigning() ? Slice(nullptr, 0) : pk.slice());
575
576 stTx = std::make_shared<STTx>(std::move(parsed.object.value()));
577 }
578 catch (STObject::FieldErr& err)
579 {
581 }
582 catch (std::exception&)
583 {
584 return RPC::make_error(rpcINTERNAL, "Exception occurred constructing serialized transaction");
585 }
586
587 std::string reason;
588 if (!passesLocalChecks(*stTx, reason))
589 return RPC::make_error(rpcINVALID_PARAMS, reason);
590
591 // If multisign then return multiSignature, else set TxnSignature field.
592 if (signingArgs.isMultiSigning())
593 {
594 Serializer s = buildMultiSigningData(*stTx, signingArgs.getSigner());
595
596 auto multisig = xrpl::sign(pk, sk, s.slice());
597
598 signingArgs.moveMultiSignature(std::move(multisig));
599 }
600 else if (signingArgs.isSingleSigning())
601 {
602 stTx->sign(pk, sk, signatureTarget);
603 }
604
605 return transactionPreProcessResult{std::move(stTx)};
606}
607
610{
612
613 // Turn the passed in STTx into a Transaction.
614 Transaction::pointer tpTrans;
615 {
616 std::string reason;
617 tpTrans = std::make_shared<Transaction>(stTx, reason, app);
618 if (tpTrans->getStatus() != NEW)
619 {
620 ret.first = RPC::make_error(rpcINTERNAL, "Unable to construct transaction: " + reason);
621 return ret;
622 }
623 }
624 try
625 {
626 // Make sure the Transaction we just built is legit by serializing it
627 // and then de-serializing it. If the result isn't equivalent
628 // to the initial transaction then there's something wrong with the
629 // passed-in STTx.
630 {
631 Serializer s;
632 tpTrans->getSTransaction()->add(s);
633 Blob transBlob = s.getData();
634 SerialIter sit{makeSlice(transBlob)};
635
636 // Check the signature if that's called for.
637 auto sttxNew = std::make_shared<STTx const>(sit);
638 if (!app.checkSigs())
639 forceValidity(app.getHashRouter(), sttxNew->getTransactionID(), Validity::SigGoodOnly);
640 if (checkValidity(app.getHashRouter(), *sttxNew, rules, app.config()).first != Validity::Valid)
641 {
642 ret.first = RPC::make_error(rpcINTERNAL, "Invalid signature.");
643 return ret;
644 }
645
646 std::string reason;
647 auto tpTransNew = std::make_shared<Transaction>(sttxNew, reason, app);
648
649 if (tpTransNew)
650 {
651 if (!tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
652 {
653 tpTransNew.reset();
654 }
655 tpTrans = std::move(tpTransNew);
656 }
657 }
658 }
659 catch (std::exception&)
660 {
661 // Assume that any exceptions are related to transaction sterilization.
662 tpTrans.reset();
663 }
664
665 if (!tpTrans)
666 {
667 ret.first = RPC::make_error(rpcINTERNAL, "Unable to sterilize transaction.");
668 return ret;
669 }
670 ret.second = std::move(tpTrans);
671 return ret;
672}
673
674static Json::Value
676{
677 Json::Value jvResult;
678 try
679 {
680 if (apiVersion > 1)
681 {
682 jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::disable_API_prior_V2);
683 jvResult[jss::hash] = to_string(tpTrans->getID());
684 }
685 else
686 jvResult[jss::tx_json] = tpTrans->getJson(JsonOptions::none);
687
688 RPC::insertDeliverMax(jvResult[jss::tx_json], tpTrans->getSTransaction()->getTxnType(), apiVersion);
689
690 jvResult[jss::tx_blob] = strHex(tpTrans->getSTransaction()->getSerializer().peekData());
691
692 if (temUNCERTAIN != tpTrans->getResult())
693 {
694 std::string sToken;
695 std::string sHuman;
696
697 transResultInfo(tpTrans->getResult(), sToken, sHuman);
698
699 jvResult[jss::engine_result] = sToken;
700 jvResult[jss::engine_result_code] = tpTrans->getResult();
701 jvResult[jss::engine_result_message] = sHuman;
702 }
703 }
704 catch (std::exception&)
705 {
706 jvResult = RPC::make_error(rpcINTERNAL, "Exception occurred during JSON handling.");
707 }
708 return jvResult;
709}
710
711} // namespace detail
712
713//------------------------------------------------------------------------------
714
715[[nodiscard]] static XRPAmount
716getTxFee(Application const& app, Config const& config, Json::Value tx)
717{
718 auto const& ledger = app.openLedger().current();
719 // autofilling only needed in this function so that the `STParsedJSONObject`
720 // parsing works properly it should not be modifying the actual `tx` object
721 if (!tx.isMember(jss::Fee))
722 {
723 tx[jss::Fee] = "0";
724 }
725
726 if (!tx.isMember(jss::Sequence))
727 {
728 tx[jss::Sequence] = "0";
729 }
730
731 if (!tx.isMember(jss::SigningPubKey))
732 {
733 tx[jss::SigningPubKey] = "";
734 }
735
736 if (!tx.isMember(jss::TxnSignature))
737 {
738 tx[jss::TxnSignature] = "";
739 }
740
741 if (tx.isMember(jss::Signers))
742 {
743 if (!tx[jss::Signers].isArray())
744 return config.FEES.reference_fee;
745
746 if (tx[jss::Signers].size() > STTx::maxMultiSigners)
747 return config.FEES.reference_fee;
748
749 // check multi-signed signers
750 for (auto& signer : tx[jss::Signers])
751 {
752 if (!signer.isMember(jss::Signer) || !signer[jss::Signer].isObject())
753 return config.FEES.reference_fee;
754 if (!signer[jss::Signer].isMember(jss::SigningPubKey))
755 {
756 // autofill SigningPubKey
757 signer[jss::Signer][jss::SigningPubKey] = "";
758 }
759 if (!signer[jss::Signer].isMember(jss::TxnSignature))
760 {
761 // autofill TxnSignature
762 signer[jss::Signer][jss::TxnSignature] = "";
763 }
764 }
765 }
766
767 STParsedJSONObject parsed(std::string(jss::tx_json), tx);
768 if (!parsed.object.has_value())
769 {
770 return config.FEES.reference_fee;
771 }
772
773 try
774 {
775 STTx const& stTx = STTx(std::move(parsed.object.value()));
776 std::string reason;
777 if (!passesLocalChecks(stTx, reason))
778 return config.FEES.reference_fee;
779
780 return calculateBaseFee(*app.openLedger().current(), stTx);
781 }
782 catch (std::exception& e)
783 {
784 return config.FEES.reference_fee;
785 }
786}
787
790 Role const role,
791 Config const& config,
792 LoadFeeTrack const& feeTrack,
793 TxQ const& txQ,
794 Application const& app,
795 Json::Value const& tx,
796 int mult,
797 int div)
798{
799 XRPAmount const feeDefault = getTxFee(app, config, tx);
800
801 auto ledger = app.openLedger().current();
802 // Administrative and identified endpoints are exempt from local fees.
803 XRPAmount const loadFee = scaleFeeLoad(feeDefault, feeTrack, ledger->fees(), isUnlimited(role));
804 XRPAmount fee = loadFee;
805 {
806 auto const metrics = txQ.getMetrics(*ledger);
807 auto const baseFee = ledger->fees().base;
808 auto escalatedFee = toDrops(metrics.openLedgerFeeLevel - FeeLevel64(1), baseFee) + 1;
809 fee = std::max(fee, escalatedFee);
810 }
811
812 auto const limit = mulDiv(feeDefault, mult, div);
813 if (!limit)
814 Throw<std::overflow_error>("mulDiv");
815
816 if (fee > *limit)
817 {
819 ss << "Fee of " << fee << " exceeds the requested tx limit of " << *limit;
820 return RPC::make_error(rpcHIGH_FEE, ss.str());
821 }
822
823 return fee.jsonClipped();
824}
825
828 Json::Value& request,
829 Role const role,
830 bool doAutoFill,
831 Config const& config,
832 LoadFeeTrack const& feeTrack,
833 TxQ const& txQ,
834 Application const& app)
835{
836 Json::Value& tx(request[jss::tx_json]);
837 if (tx.isMember(jss::Fee))
838 return Json::Value();
839
840 if (!doAutoFill)
841 return RPC::missing_field_error("tx_json.Fee");
842
845 if (request.isMember(jss::fee_mult_max))
846 {
847 if (request[jss::fee_mult_max].isInt())
848 {
849 mult = request[jss::fee_mult_max].asInt();
850 if (mult < 0)
851 return RPC::make_error(
852 rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_mult_max, "a positive integer"));
853 }
854 else
855 {
856 return RPC::make_error(rpcHIGH_FEE, RPC::expected_field_message(jss::fee_mult_max, "a positive integer"));
857 }
858 }
859 if (request.isMember(jss::fee_div_max))
860 {
861 if (request[jss::fee_div_max].isInt())
862 {
863 div = request[jss::fee_div_max].asInt();
864 if (div <= 0)
865 return RPC::make_error(
866 rpcINVALID_PARAMS, RPC::expected_field_message(jss::fee_div_max, "a positive integer"));
867 }
868 else
869 {
870 return RPC::make_error(rpcHIGH_FEE, RPC::expected_field_message(jss::fee_div_max, "a positive integer"));
871 }
872 }
873
874 auto feeOrError = getCurrentNetworkFee(role, config, feeTrack, txQ, app, tx, mult, div);
875 if (feeOrError.isMember(jss::error))
876 return feeOrError;
877 tx[jss::Fee] = std::move(feeOrError);
878 return Json::Value();
879}
880
881//------------------------------------------------------------------------------
882
886 Json::Value jvRequest,
887 unsigned apiVersion,
888 NetworkOPs::FailHard failType,
889 Role role,
890 std::chrono::seconds validatedLedgerAge,
891 Application& app)
892{
893 using namespace detail;
894
895 auto j = app.journal("RPCHandler");
896 JLOG(j.debug()) << "transactionSign: " << jvRequest;
897
898 // Add and amend fields based on the transaction type.
899 SigningForParams signForParams;
900 transactionPreProcessResult preprocResult =
901 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
902
903 if (!preprocResult.second)
904 return preprocResult.first;
905
907 // Make sure the STTx makes a legitimate Transaction.
909 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
910
911 if (!txn.second)
912 return txn.first;
913
914 return transactionFormatResultImpl(txn.second, apiVersion);
915}
916
920 Json::Value jvRequest,
921 unsigned apiVersion,
922 NetworkOPs::FailHard failType,
923 Role role,
924 std::chrono::seconds validatedLedgerAge,
925 Application& app,
926 ProcessTransactionFn const& processTransaction)
927{
928 using namespace detail;
929
930 auto const& ledger = app.openLedger().current();
931 auto j = app.journal("RPCHandler");
932 JLOG(j.debug()) << "transactionSubmit: " << jvRequest;
933
934 // Add and amend fields based on the transaction type.
935 SigningForParams signForParams;
936 transactionPreProcessResult preprocResult =
937 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
938
939 if (!preprocResult.second)
940 return preprocResult.first;
941
942 // Make sure the STTx makes a legitimate Transaction.
944 transactionConstructImpl(preprocResult.second, ledger->rules(), app);
945
946 if (!txn.second)
947 return txn.first;
948
949 // Finally, submit the transaction.
950 try
951 {
952 // FIXME: For performance, should use async interface
953 processTransaction(txn.second, isUnlimited(role), true, failType);
954 }
955 catch (std::exception&)
956 {
957 return RPC::make_error(rpcINTERNAL, "Exception occurred during transaction submission.");
958 }
959
960 return transactionFormatResultImpl(txn.second, apiVersion);
961}
962
963namespace detail {
964// There are a some field checks shared by transactionSignFor
965// and transactionSubmitMultiSigned. Gather them together here.
966static Json::Value
968{
969 if (!jvRequest.isMember(jss::tx_json))
970 return RPC::missing_field_error(jss::tx_json);
971
972 Json::Value const& tx_json(jvRequest[jss::tx_json]);
973
974 if (!tx_json.isObject())
975 return RPC::invalid_field_message(jss::tx_json);
976
977 // There are a couple of additional fields we need to check before
978 // we serialize. If we serialize first then we generate less useful
979 // error messages.
980 if (!tx_json.isMember(jss::Sequence))
981 return RPC::missing_field_error("tx_json.Sequence");
982
983 if (!tx_json.isMember(sfSigningPubKey.getJsonName()))
984 return RPC::missing_field_error("tx_json.SigningPubKey");
985
986 // Multi-signing into a signature_target object field is fine,
987 // because it means the signature is not for the transaction
988 // Account.
989 if (!jvRequest.isMember(jss::signature_target) && !tx_json[sfSigningPubKey.getJsonName()].asString().empty())
990 return RPC::make_error(rpcINVALID_PARAMS, "When multi-signing 'tx_json.SigningPubKey' must be empty.");
991
992 return Json::Value();
993}
994
995// Sort and validate an stSigners array.
996//
997// Returns a null Json::Value if there are no errors.
998static Json::Value
999sortAndValidateSigners(STArray& signers, AccountID const& signingForID)
1000{
1001 if (signers.empty())
1002 return RPC::make_param_error("Signers array may not be empty.");
1003
1004 // Signers must be sorted by Account.
1005 std::sort(signers.begin(), signers.end(), [](STObject const& a, STObject const& b) {
1006 return (a[sfAccount] < b[sfAccount]);
1007 });
1008
1009 // Signers may not contain any duplicates.
1010 auto const dupIter = std::adjacent_find(signers.begin(), signers.end(), [](STObject const& a, STObject const& b) {
1011 return (a[sfAccount] == b[sfAccount]);
1012 });
1013
1014 if (dupIter != signers.end())
1015 {
1017 err << "Duplicate Signers:Signer:Account entries (" << toBase58((*dupIter)[sfAccount]) << ") are not allowed.";
1018 return RPC::make_param_error(err.str());
1019 }
1020
1021 // An account may not sign for itself.
1022 if (signers.end() != std::find_if(signers.begin(), signers.end(), [&signingForID](STObject const& elem) {
1023 return elem[sfAccount] == signingForID;
1024 }))
1025 {
1027 err << "A Signer may not be the transaction's Account (" << toBase58(signingForID) << ").";
1028 return RPC::make_param_error(err.str());
1029 }
1030 return {};
1031}
1032
1033} // namespace detail
1034
1038 Json::Value jvRequest,
1039 unsigned apiVersion,
1040 NetworkOPs::FailHard failType,
1041 Role role,
1042 std::chrono::seconds validatedLedgerAge,
1043 Application& app)
1044{
1045 auto const& ledger = app.openLedger().current();
1046 auto j = app.journal("RPCHandler");
1047 JLOG(j.debug()) << "transactionSignFor: " << jvRequest;
1048
1049 // Verify presence of the signer's account field.
1050 char const accountField[] = "account";
1051
1052 if (!jvRequest.isMember(accountField))
1053 return RPC::missing_field_error(accountField);
1054
1055 // Turn the signer's account into an AccountID for multi-sign.
1056 auto const signerAccountID = parseBase58<AccountID>(jvRequest[accountField].asString());
1057 if (!signerAccountID)
1058 {
1060 }
1061
1062 if (!jvRequest.isMember(jss::tx_json))
1063 return RPC::missing_field_error(jss::tx_json);
1064
1065 {
1066 Json::Value& tx_json(jvRequest[jss::tx_json]);
1067
1068 if (!tx_json.isObject())
1069 return RPC::object_field_error(jss::tx_json);
1070
1071 // If the tx_json.SigningPubKey field is missing,
1072 // insert an empty one.
1073 if (!tx_json.isMember(sfSigningPubKey.getJsonName()))
1074 tx_json[sfSigningPubKey.getJsonName()] = "";
1075 }
1076
1077 // When multi-signing, the "Sequence" and "SigningPubKey" fields must
1078 // be passed in by the caller.
1079 using namespace detail;
1080 {
1081 Json::Value err = checkMultiSignFields(jvRequest);
1082 if (RPC::contains_error(err))
1083 return err;
1084 }
1085
1086 // Add and amend fields based on the transaction type.
1087 SigningForParams signForParams(*signerAccountID);
1088
1089 transactionPreProcessResult preprocResult =
1090 transactionPreProcessImpl(jvRequest, role, signForParams, validatedLedgerAge, app);
1091
1092 if (!preprocResult.second)
1093 return preprocResult.first;
1094
1095 XRPL_ASSERT(signForParams.validMultiSign(), "xrpl::RPC::transactionSignFor : valid multi-signature");
1096
1097 {
1098 std::shared_ptr<SLE const> account_state = ledger->read(keylet::account(*signerAccountID));
1099 // Make sure the account and secret belong together.
1100 auto const err = acctMatchesPubKey(account_state, *signerAccountID, signForParams.getPublicKey());
1101
1102 if (err != rpcSUCCESS)
1103 return rpcError(err);
1104 }
1105
1106 // Inject the newly generated signature into tx_json.Signers.
1107 auto& sttx = preprocResult.second;
1108 {
1109 // Make the signer object that we'll inject.
1110 STObject signer = STObject::makeInnerObject(sfSigner);
1111 signer[sfAccount] = *signerAccountID;
1112 signer.setFieldVL(sfTxnSignature, signForParams.getSignature());
1113 signer.setFieldVL(sfSigningPubKey, signForParams.getPublicKey().slice());
1114
1115 STObject& sigTarget = [&]() -> STObject& {
1116 auto const target = signForParams.getSignatureTarget();
1117 if (target)
1118 return sttx->peekFieldObject(*target);
1119 return *sttx;
1120 }();
1121 // If there is not yet a Signers array, make one.
1122 if (!sigTarget.isFieldPresent(sfSigners))
1123 sigTarget.setFieldArray(sfSigners, {});
1124
1125 auto& signers = sigTarget.peekFieldArray(sfSigners);
1126 signers.emplace_back(std::move(signer));
1127
1128 // The array must be sorted and validated.
1129 auto err = sortAndValidateSigners(signers, (*sttx)[sfAccount]);
1130 if (RPC::contains_error(err))
1131 return err;
1132 }
1133
1134 // Make sure the STTx makes a legitimate Transaction.
1135 std::pair<Json::Value, Transaction::pointer> txn = transactionConstructImpl(sttx, ledger->rules(), app);
1136
1137 if (!txn.second)
1138 return txn.first;
1139
1140 return transactionFormatResultImpl(txn.second, apiVersion);
1141}
1142
1146 Json::Value jvRequest,
1147 unsigned apiVersion,
1148 NetworkOPs::FailHard failType,
1149 Role role,
1150 std::chrono::seconds validatedLedgerAge,
1151 Application& app,
1152 ProcessTransactionFn const& processTransaction)
1153{
1154 auto const& ledger = app.openLedger().current();
1155 auto j = app.journal("RPCHandler");
1156 JLOG(j.debug()) << "transactionSubmitMultiSigned: " << jvRequest;
1157
1158 // When multi-signing, the "Sequence" and "SigningPubKey" fields must
1159 // be passed in by the caller.
1160 using namespace detail;
1161 {
1162 Json::Value err = checkMultiSignFields(jvRequest);
1163 if (RPC::contains_error(err))
1164 return err;
1165 }
1166
1167 Json::Value& tx_json(jvRequest["tx_json"]);
1168
1169 auto [txJsonResult, srcAddressID] = checkTxJsonFields(
1170 tx_json,
1171 role,
1172 true,
1173 validatedLedgerAge,
1174 app.config(),
1175 app.getFeeTrack(),
1176 getAPIVersionNumber(jvRequest, app.config().BETA_RPC_API));
1177
1178 if (RPC::contains_error(txJsonResult))
1179 return std::move(txJsonResult);
1180
1181 std::shared_ptr<SLE const> sle = ledger->read(keylet::account(srcAddressID));
1182
1183 if (!sle)
1184 {
1185 // If did not find account, error.
1186 JLOG(j.debug()) << "transactionSubmitMultiSigned: Failed to find source account "
1187 << "in current ledger: " << toBase58(srcAddressID);
1188
1190 }
1191
1192 {
1193 Json::Value err = checkFee(jvRequest, role, false, app.config(), app.getFeeTrack(), app.getTxQ(), app);
1194
1195 if (RPC::contains_error(err))
1196 return err;
1197
1198 err = checkPayment(jvRequest, tx_json, srcAddressID, role, app, false);
1199
1200 if (RPC::contains_error(err))
1201 return err;
1202 }
1203
1204 // Grind through the JSON in tx_json to produce a STTx.
1206 {
1207 STParsedJSONObject parsedTx_json("tx_json", tx_json);
1208 if (!parsedTx_json.object)
1209 {
1210 Json::Value jvResult;
1211 jvResult["error"] = parsedTx_json.error["error"];
1212 jvResult["error_code"] = parsedTx_json.error["error_code"];
1213 jvResult["error_message"] = parsedTx_json.error["error_message"];
1214 return jvResult;
1215 }
1216 try
1217 {
1218 stTx = std::make_shared<STTx>(std::move(parsedTx_json.object.value()));
1219 }
1220 catch (STObject::FieldErr& err)
1221 {
1222 return RPC::make_error(rpcINVALID_PARAMS, err.what());
1223 }
1224 catch (std::exception& ex)
1225 {
1226 std::string reason(ex.what());
1227 return RPC::make_error(rpcINTERNAL, "Exception while serializing transaction: " + reason);
1228 }
1229 std::string reason;
1230 if (!passesLocalChecks(*stTx, reason))
1231 return RPC::make_error(rpcINVALID_PARAMS, reason);
1232 }
1233
1234 // Validate the fields in the serialized transaction.
1235 {
1236 // We now have the transaction text serialized and in the right format.
1237 // Verify the values of select fields.
1238 //
1239 // The SigningPubKey must be present but empty.
1240 if (!stTx->getFieldVL(sfSigningPubKey).empty())
1241 {
1243 err << "Invalid " << sfSigningPubKey.fieldName << " field. Field must be empty when multi-signing.";
1244 return RPC::make_error(rpcINVALID_PARAMS, err.str());
1245 }
1246
1247 // There may not be a TxnSignature field.
1248 if (stTx->isFieldPresent(sfTxnSignature))
1250
1251 // The Fee field must be in XRP and greater than zero.
1252 auto const fee = stTx->getFieldAmount(sfFee);
1253
1254 if (!isLegalNet(fee))
1255 {
1257 err << "Invalid " << sfFee.fieldName << " field. Fees must be specified in XRP.";
1258 return RPC::make_error(rpcINVALID_PARAMS, err.str());
1259 }
1260 if (fee <= STAmount{0})
1261 {
1263 err << "Invalid " << sfFee.fieldName << " field. Fees must be greater than zero.";
1264 return RPC::make_error(rpcINVALID_PARAMS, err.str());
1265 }
1266 }
1267
1268 // Verify that the Signers field is present.
1269 if (!stTx->isFieldPresent(sfSigners))
1270 return RPC::missing_field_error("tx_json.Signers");
1271
1272 // If the Signers field is present the SField guarantees it to be an array.
1273 // Get a reference to the Signers array so we can verify and sort it.
1274 auto& signers = stTx->peekFieldArray(sfSigners);
1275
1276 if (signers.empty())
1277 return RPC::make_param_error("tx_json.Signers array may not be empty.");
1278
1279 // The Signers array may only contain Signer objects.
1280 if (std::find_if_not(signers.begin(), signers.end(), [](STObject const& obj) {
1281 return (
1282 // A Signer object always contains these fields and no
1283 // others.
1284 obj.isFieldPresent(sfAccount) && obj.isFieldPresent(sfSigningPubKey) &&
1285 obj.isFieldPresent(sfTxnSignature) && obj.getCount() == 3);
1286 }) != signers.end())
1287 {
1288 return RPC::make_param_error("Signers array may only contain Signer entries.");
1289 }
1290
1291 // The array must be sorted and validated.
1292 auto err = sortAndValidateSigners(signers, srcAddressID);
1293 if (RPC::contains_error(err))
1294 return err;
1295
1296 // Make sure the SerializedTransaction makes a legitimate Transaction.
1297 std::pair<Json::Value, Transaction::pointer> txn = transactionConstructImpl(stTx, ledger->rules(), app);
1298
1299 if (!txn.second)
1300 return txn.first;
1301
1302 // Finally, submit the transaction.
1303 try
1304 {
1305 // FIXME: For performance, should use async interface
1306 processTransaction(txn.second, isUnlimited(role), true, failType);
1307 }
1308 catch (std::exception&)
1309 {
1310 return RPC::make_error(rpcINTERNAL, "Exception occurred during transaction submission.");
1311 }
1312
1313 return transactionFormatResultImpl(txn.second, apiVersion);
1314}
1315
1316} // namespace RPC
1317} // namespace xrpl
T adjacent_find(T... args)
Represents a JSON value.
Definition json_value.h:131
Int asInt() const
bool isString() const
bool isObject() const
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool asBool() const
bool isMember(char const *key) const
Return true if the object has a member named key.
virtual TxQ & getTxQ()=0
virtual HashRouter & getHashRouter()=0
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual bool checkSigs() const =0
virtual beast::Journal journal(std::string const &name)=0
virtual OpenLedger & openLedger()=0
Like std::vector<char> but better.
Definition Buffer.h:17
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
Definition Buffer.h:105
uint32_t NETWORK_ID
Definition Config.h:138
int PATH_SEARCH_OLD
Definition Config.h:177
bool BETA_RPC_API
Definition Config.h:268
bool standalone() const
Definition Config.h:312
FeeSetup FEES
Definition Config.h:185
SOTemplate const * findSOTemplateBySField(SField const &sField) const
static InnerObjectFormats const & getInstance()
Currency currency
Definition Issue.h:16
AccountID account
Definition Issue.h:17
Manages the current fee schedule.
bool isLoadedCluster() const
std::shared_ptr< OpenView const > current() const
Returns a view to the current open ledger.
Calculates payment paths.
Definition Pathfinder.h:21
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:43
Slice slice() const noexcept
Definition PublicKey.h:104
void setSignatureTarget(std::optional< std::reference_wrapper< SField const > > const &field)
std::optional< PublicKey > multiSignPublicKey_
AccountID const & getSigner() const
std::optional< std::reference_wrapper< SField const > > const & getSignatureTarget() const
void setPublicKey(PublicKey const &multiSignPublicKey)
PublicKey const & getPublicKey() 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.
Definition Rules.h:19
static SField const & getField(int fieldCode)
Definition SField.cpp:94
Issue const & issue() const
Definition STAmount.h:455
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:555
bool native() const noexcept
Definition STAmount.h:417
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:760
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:802
STObject & peekFieldObject(SField const &field)
Definition STObject.cpp:450
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:72
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.
bool empty() const
Definition STPathSet.h:468
Json::Value getJson(JsonOptions) const override
static constexpr std::size_t maxMultiSigners
Definition STTx.h:35
A secret key.
Definition SecretKey.h:19
constexpr std::uint32_t value() const
Definition SeqProxy.h:63
Slice slice() const noexcept
Definition Serializer.h:45
Blob getData() const
Definition Serializer.h:182
An immutable linear range of bytes.
Definition Slice.h:27
Transaction Queue.
Definition TxQ.h:42
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1603
SeqProxy nextQueuableSeq(std::shared_ptr< SLE const > const &sleAccount) const
Return the next sequence that would go in the TxQ for an account.
Definition TxQ.cpp:1452
Json::Value jsonClipped() const
Definition XRPAmount.h:197
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:472
T empty(T... args)
T find_if(T... args)
T is_same_v
T max(T... args)
STL namespace.
static int constexpr defaultAutoFillFeeMultiplier
static int constexpr defaultAutoFillFeeDivisor
static transactionPreProcessResult transactionPreProcessImpl(Json::Value &params, Role role, SigningForParams &signingArgs, std::chrono::seconds validatedLedgerAge, Application &app)
static Json::Value checkPayment(Json::Value const &params, 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)
Definition ErrorCodes.h:269
std::string expected_field_message(std::string const &name, std::string const &type)
Definition ErrorCodes.h:281
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)
Definition ErrorCodes.h:221
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
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)
Definition ErrorCodes.h:227
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)
Definition ErrorCodes.h:245
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
Definition ApiVersion.h:39
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition ErrorCodes.h:215
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)
Definition ErrorCodes.h:257
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.
unsigned int getAPIVersionNumber(Json::Value const &jv, bool betaEnabled)
Retrieve the api version number from the json value.
Definition ApiVersion.h:98
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.
Definition Indexes.cpp:160
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
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)
Definition base_uint.h:598
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
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:567
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition TxQ.h:816
bool transResultInfo(TER code, std::string &token, std::string &text)
Definition TER.cpp:228
Role
Indicates the level of administrative permission to grant.
Definition Role.h:25
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
bool passesLocalChecks(STObject const &st, std::string &)
Definition STTx.cpp:737
AccountID calcAccountID(PublicKey const &pk)
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules, Config const &config)
Checks transaction signature and local checks.
Definition apply.cpp:21
@ temUNCERTAIN
Definition TER.h:104
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.
@ lsfDisableMaster
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:89
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)
Definition Slice.h:214
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:96
error_code_i
Definition ErrorCodes.h:21
@ rpcTOO_BUSY
Definition ErrorCodes.h:37
@ rpcNO_CURRENT
Definition ErrorCodes.h:46
@ rpcDELEGATE_ACT_NOT_FOUND
Definition ErrorCodes.h:104
@ rpcSIGNING_MALFORMED
Definition ErrorCodes.h:99
@ rpcHIGH_FEE
Definition ErrorCodes.h:39
@ rpcMASTER_DISABLED
Definition ErrorCodes.h:55
@ rpcINTERNAL
Definition ErrorCodes.h:111
@ rpcALREADY_SINGLE_SIG
Definition ErrorCodes.h:73
@ rpcSRC_ACT_NOT_FOUND
Definition ErrorCodes.h:103
@ rpcSRC_ACT_MALFORMED
Definition ErrorCodes.h:101
@ rpcNOT_SYNCED
Definition ErrorCodes.h:48
@ rpcDOMAIN_MALFORMED
Definition ErrorCodes.h:139
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:65
@ rpcSRC_ACT_MISSING
Definition ErrorCodes.h:102
@ rpcBAD_SECRET
Definition ErrorCodes.h:79
@ rpcALREADY_MULTISIG
Definition ErrorCodes.h:72
@ rpcSUCCESS
Definition ErrorCodes.h:25
Serializer buildMultiSigningData(STObject const &obj, AccountID const &signingID)
Return a Serializer suitable for computing a multisigning TxnSignature.
Definition Sign.cpp:67
T reset(T... args)
T sort(T... args)
T str(T... args)
XRPAmount reference_fee
The cost of a reference transaction in drops.
Definition Config.h:49
transactionPreProcessResult & operator=(transactionPreProcessResult &&)=delete
transactionPreProcessResult(std::shared_ptr< STTx > &&st)
transactionPreProcessResult & operator=(transactionPreProcessResult const &)=delete
transactionPreProcessResult(transactionPreProcessResult &&rhs)=default
transactionPreProcessResult(transactionPreProcessResult const &)=delete
T what(T... args)