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