diff --git a/include/xrpl/protocol/TxMeta.h b/include/xrpl/protocol/TxMeta.h index 02fde2ffe5..eb594fcc57 100644 --- a/include/xrpl/protocol/TxMeta.h +++ b/include/xrpl/protocol/TxMeta.h @@ -46,10 +46,7 @@ private: CtorHelper); public: - TxMeta( - uint256 const& transactionID, - std::uint32_t ledger, - std::optional parentBatchId = std::nullopt); + TxMeta(uint256 const& transactionID, std::uint32_t ledger); TxMeta(uint256 const& txID, std::uint32_t ledger, Blob const&); TxMeta(uint256 const& txID, std::uint32_t ledger, std::string const&); TxMeta(uint256 const& txID, std::uint32_t ledger, STObject const&); @@ -136,7 +133,7 @@ public: void setParentBatchId(uint256 const& parentBatchId) { - mParentBatchId = parentBatchId; + parentBatchId_ = parentBatchId; } uint256 @@ -145,13 +142,33 @@ public: XRPL_ASSERT( hasParentBatchId(), "ripple::TxMeta::getParentBatchId : non-null batch id"); - return *mParentBatchId; + return *parentBatchId_; } bool hasParentBatchId() const { - return static_cast(mParentBatchId); + return static_cast(parentBatchId_); + } + + void + setGasUsed(std::uint32_t const& gasUsed) + { + gasUsed_ = gasUsed; + } + + std::uint32_t + getGasUsed() const + { + XRPL_ASSERT( + hasGasUsed(), "ripple::TxMeta::getGasUsed : non-null batch id"); + return *gasUsed_; + } + + bool + hasGasUsed() const + { + return static_cast(gasUsed_); } private: @@ -161,7 +178,8 @@ private: int mResult; std::optional mDelivered; - std::optional mParentBatchId; + std::optional gasUsed_; + std::optional parentBatchId_; STArray mNodes; }; diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index faeff4ebb2..155d0e8663 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -118,6 +118,7 @@ TYPED_SFIELD(sfExtensionComputeLimit, UINT32, 53) TYPED_SFIELD(sfExtensionSizeLimit, UINT32, 54) TYPED_SFIELD(sfGasPrice, UINT32, 55) TYPED_SFIELD(sfComputationAllowance, UINT32, 56) +TYPED_SFIELD(sfGasUsed, UINT32, 57) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) diff --git a/src/libxrpl/protocol/TxMeta.cpp b/src/libxrpl/protocol/TxMeta.cpp index 2083fc8eaf..fb703d9435 100644 --- a/src/libxrpl/protocol/TxMeta.cpp +++ b/src/libxrpl/protocol/TxMeta.cpp @@ -56,9 +56,10 @@ TxMeta::TxMeta( if (obj.isFieldPresent(sfDeliveredAmount)) setDeliveredAmount(obj.getFieldAmount(sfDeliveredAmount)); - if (obj.isFieldPresent(sfParentBatchID)) setParentBatchId(obj.getFieldH256(sfParentBatchID)); + if (obj.isFieldPresent(sfGasUsed)) + setGasUsed(obj.getFieldU32(sfGasUsed)); } TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj) @@ -82,6 +83,9 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj) if (obj.isFieldPresent(sfParentBatchID)) setParentBatchId(obj.getFieldH256(sfParentBatchID)); + + if (obj.isFieldPresent(sfGasUsed)) + setGasUsed(obj.getFieldU32(sfGasUsed)); } TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec) @@ -97,15 +101,11 @@ TxMeta::TxMeta( { } -TxMeta::TxMeta( - uint256 const& transactionID, - std::uint32_t ledger, - std::optional parentBatchId) +TxMeta::TxMeta(uint256 const& transactionID, std::uint32_t ledger) : mTransactionID(transactionID) , mLedger(ledger) , mIndex(static_cast(-1)) , mResult(255) - , mParentBatchId(parentBatchId) , mNodes(sfAffectedNodes) { mNodes.reserve(32); @@ -241,9 +241,10 @@ TxMeta::getAsObject() const metaData.emplace_back(mNodes); if (hasDeliveredAmount()) metaData.setFieldAmount(sfDeliveredAmount, getDeliveredAmount()); - if (hasParentBatchId()) metaData.setFieldH256(sfParentBatchID, getParentBatchId()); + if (hasGasUsed()) + metaData.setFieldU32(sfGasUsed, getGasUsed()); return metaData; } diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index c326f8c996..df505b317b 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1863,7 +1863,7 @@ struct Escrow_test : public beast::unit_test::suite XRPAmount const txnFees = env.current()->fees().base + 1000; env(finish(carol, alice, 1), fee(txnFees), - comp_allowance(110), + comp_allowance(4), ter(temDISABLED)); env.close(); } @@ -1914,12 +1914,12 @@ struct Escrow_test : public beast::unit_test::suite { // not enough fees - // This function takes 110 gas + // This function takes 4 gas // In testing, 1 gas costs 1 drop - auto const finishFee = env.current()->fees().base + 109; + auto const finishFee = env.current()->fees().base + 3; env(finish(carol, alice, seq), fee(finishFee), - comp_allowance(110), + comp_allowance(4), ter(telINSUF_FEE_P)); } @@ -1968,6 +1968,7 @@ struct Escrow_test : public beast::unit_test::suite // unsafe { host_lib::getLedgerSqn() >= 5} // } static auto wasmHex = ledgerSqnHex; + std::uint32_t constexpr allowance = 4; { // basic FinishFunction situation @@ -1990,31 +1991,34 @@ struct Escrow_test : public beast::unit_test::suite env.require(balance(carol, XRP(5000))); env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env(finish(alice, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env(finish(alice, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env.close(); env(finish(alice, alice, seq), fee(txnFees), - comp_allowance(110), + comp_allowance(allowance), ter(tesSUCCESS)); - env.close(); + + auto const txMeta = env.meta(); + if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) + BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance); BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); } @@ -2046,38 +2050,42 @@ struct Escrow_test : public beast::unit_test::suite // no fulfillment provided, function fails env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecCRYPTOCONDITION_ERROR)); // fulfillment provided, function fails env(finish(carol, alice, seq), condition(cb1), fulfillment(fb1), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env.close(); // no fulfillment provided, function succeeds env(finish(alice, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecCRYPTOCONDITION_ERROR)); // wrong fulfillment provided, function succeeds env(finish(alice, alice, seq), condition(cb1), fulfillment(fb2), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecCRYPTOCONDITION_ERROR)); // fulfillment provided, function succeeds, tx succeeds env(finish(alice, alice, seq), condition(cb1), fulfillment(fb1), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tesSUCCESS)); - env.close(); + auto const txMeta = env.meta(); + if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) + BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance); + + env.close(); BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); } } @@ -2106,21 +2114,26 @@ struct Escrow_test : public beast::unit_test::suite // finish time hasn't passed, function fails env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(4), fee(txnFees + 1), ter(tecNO_PERMISSION)); env.close(); // finish time hasn't passed, function succeeds for (; env.now() < ts; env.close()) env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(4), fee(txnFees + 2), ter(tecNO_PERMISSION)); env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(4), fee(txnFees + 1), ter(tesSUCCESS)); + + auto const txMeta = env.meta(); + if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) + BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); } } @@ -2148,24 +2161,28 @@ struct Escrow_test : public beast::unit_test::suite // finish time hasn't passed, function fails env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecNO_PERMISSION)); env.close(); // finish time has passed, function fails env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); env.close(); // finish time has passed, function succeeds, tx succeeds env(finish(carol, alice, seq), - comp_allowance(110), + comp_allowance(allowance), fee(txnFees), ter(tesSUCCESS)); - env.close(); + auto const txMeta = env.meta(); + if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) + BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance); + + env.close(); BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); } } @@ -2179,7 +2196,6 @@ struct Escrow_test : public beast::unit_test::suite using namespace jtx; using namespace std::chrono; - // TODO: figure out how to make this a fixture in a separate file static auto wasmHex = allHostFunctionsHex; // let sender = get_tx_account_id(); // let owner = get_current_escrow_account_id(); @@ -2218,7 +2234,8 @@ struct Escrow_test : public beast::unit_test::suite env.require(balance(alice, XRP(4000) - txnFees)); env.require(balance(carol, XRP(5000))); - auto const allowance = 40'000; + // TODO: figure out why this can't be 2412 + auto const allowance = 3'600; // FinishAfter time hasn't passed env(finish(carol, alice, seq), @@ -2232,6 +2249,7 @@ struct Escrow_test : public beast::unit_test::suite comp_allowance(allowance), fee(txnFees), ter(tecWASM_REJECTED)); + env.close(); // destination balance is too high env(finish(carol, alice, seq), @@ -2256,8 +2274,12 @@ struct Escrow_test : public beast::unit_test::suite comp_allowance(allowance), fee(txnFees), ter(tesSUCCESS)); - env.close(); + auto const txMeta = env.meta(); + if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed))) + BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == 2412); + + env.close(); BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); } } diff --git a/src/xrpld/app/tx/detail/ApplyContext.h b/src/xrpld/app/tx/detail/ApplyContext.h index 720d0aeea3..a3bdf9a85a 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/src/xrpld/app/tx/detail/ApplyContext.h @@ -106,6 +106,13 @@ public: view_->deliver(amount); } + /** Sets the gas used in the metadata */ + void + setGasUsed(std::uint32_t const& gasUsed) + { + view_->setGasUsed(gasUsed); + } + /** Discard changes and start fresh. */ void discard(); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 375a77022c..42e67621e7 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -634,6 +634,8 @@ EscrowFinish::doApply() if (re.has_value()) { auto reValue = re.value().result; + // TODO: better error handling for this conversion + ctx_.setGasUsed(static_cast(re.value().cost)); JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) << ", cost: " << re.value().cost; if (!reValue) diff --git a/src/xrpld/ledger/ApplyViewImpl.h b/src/xrpld/ledger/ApplyViewImpl.h index d170cf71ff..f7e90b9d54 100644 --- a/src/xrpld/ledger/ApplyViewImpl.h +++ b/src/xrpld/ledger/ApplyViewImpl.h @@ -75,6 +75,12 @@ public: deliver_ = amount; } + void + setGasUsed(std::uint32_t const& gasUsed) + { + gasUsed_ = gasUsed; + } + /** Get the number of modified entries */ std::size_t @@ -93,6 +99,7 @@ public: private: std::optional deliver_; + std::optional gasUsed_; }; } // namespace ripple diff --git a/src/xrpld/ledger/detail/ApplyStateTable.cpp b/src/xrpld/ledger/detail/ApplyStateTable.cpp index 2a740093d9..039088205b 100644 --- a/src/xrpld/ledger/detail/ApplyStateTable.cpp +++ b/src/xrpld/ledger/detail/ApplyStateTable.cpp @@ -117,6 +117,7 @@ ApplyStateTable::apply( TER ter, std::optional const& deliver, std::optional const& parentBatchId, + std::optional const& gasUsed, bool isDryRun, beast::Journal j) { @@ -127,11 +128,14 @@ ApplyStateTable::apply( std::optional metadata; if (!to.open() || isDryRun) { - TxMeta meta(tx.getTransactionID(), to.seq(), parentBatchId); + TxMeta meta(tx.getTransactionID(), to.seq()); if (deliver) meta.setDeliveredAmount(*deliver); - + if (parentBatchId) + meta.setParentBatchId(*parentBatchId); + if (gasUsed) + meta.setGasUsed(*gasUsed); Mods newMod; for (auto& item : items_) { diff --git a/src/xrpld/ledger/detail/ApplyStateTable.h b/src/xrpld/ledger/detail/ApplyStateTable.h index 5a2e0bcf54..896705042f 100644 --- a/src/xrpld/ledger/detail/ApplyStateTable.h +++ b/src/xrpld/ledger/detail/ApplyStateTable.h @@ -73,6 +73,7 @@ public: TER ter, std::optional const& deliver, std::optional const& parentBatchId, + std::optional const& gasUsed, bool isDryRun, beast::Journal j); diff --git a/src/xrpld/ledger/detail/ApplyViewImpl.cpp b/src/xrpld/ledger/detail/ApplyViewImpl.cpp index 3fd9478b54..a90fc3fcbb 100644 --- a/src/xrpld/ledger/detail/ApplyViewImpl.cpp +++ b/src/xrpld/ledger/detail/ApplyViewImpl.cpp @@ -35,7 +35,8 @@ ApplyViewImpl::apply( bool isDryRun, beast::Journal j) { - return items_.apply(to, tx, ter, deliver_, parentBatchId, isDryRun, j); + return items_.apply( + to, tx, ter, deliver_, parentBatchId, gasUsed_, isDryRun, j); } std::size_t