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