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