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