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