diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 135a4af55..a073b8466 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1745,8 +1745,9 @@ finalizeHookResult( ApplyViewImpl& avi = dynamic_cast(applyCtx.view()); uint16_t exec_index = avi.nextHookExecutionIndex(); - uint16_t emission_count = 0; // apply emitted transactions to the ledger (by adding them to the emitted directory) if we are allowed to + std::vector emission_txnid; + if (doEmit) { DBG_PRINTF("emitted txn count: %d\n", hookResult.emittedTxn.size()); @@ -1766,7 +1767,7 @@ finalizeHookResult( if (!sleEmitted) { - ++emission_count; + emission_txnid.push_back(id); sleEmitted = std::make_shared(emittedId); // RH TODO: add a new constructor to STObject to avoid this serder thing @@ -1801,24 +1802,39 @@ finalizeHookResult( } // add a metadata entry for this hook execution result - STObject meta { sfHookExecution }; - meta.setFieldU8(sfHookResult, hookResult.exitType ); - meta.setAccountID(sfHookAccount, hookResult.account); + { + STObject meta { sfHookExecution }; + meta.setFieldU8(sfHookResult, hookResult.exitType ); + meta.setAccountID(sfHookAccount, hookResult.account); - // RH NOTE: this is probably not necessary, a direct cast should always put the (negative) 1 bit at the MSB - // however to ensure this is consistent across different arch/compilers it's done explicitly here. - uint64_t unsigned_exit_code = - ( hookResult.exitCode >= 0 ? hookResult.exitCode : - 0x8000000000000000ULL + (-1 * hookResult.exitCode )); + // RH NOTE: this is probably not necessary, a direct cast should always put the (negative) 1 bit at the MSB + // however to ensure this is consistent across different arch/compilers it's done explicitly here. + uint64_t unsigned_exit_code = + ( hookResult.exitCode >= 0 ? hookResult.exitCode : + 0x8000000000000000ULL + (-1 * hookResult.exitCode )); - meta.setFieldU64(sfHookReturnCode, unsigned_exit_code); - meta.setFieldVL(sfHookReturnString, ripple::Slice{hookResult.exitReason.data(), hookResult.exitReason.size()}); - meta.setFieldU64(sfHookInstructionCount, hookResult.instructionCount); - meta.setFieldU16(sfHookEmitCount, emission_count); // this will never wrap, hard limit - meta.setFieldU16(sfHookExecutionIndex, exec_index ); - meta.setFieldU16(sfHookStateChangeCount, hookResult.changedStateCount ); - meta.setFieldH256(sfHookHash, hookResult.hookHash); - avi.addHookMetaData(std::move(meta)); + meta.setFieldU64(sfHookReturnCode, unsigned_exit_code); + meta.setFieldVL(sfHookReturnString, ripple::Slice{hookResult.exitReason.data(), hookResult.exitReason.size()}); + meta.setFieldU64(sfHookInstructionCount, hookResult.instructionCount); + meta.setFieldU16(sfHookEmitCount, emission_txnid.size()); // this will never wrap, hard limit + meta.setFieldU16(sfHookExecutionIndex, exec_index ); + meta.setFieldU16(sfHookStateChangeCount, hookResult.changedStateCount ); + meta.setFieldH256(sfHookHash, hookResult.hookHash); + avi.addHookExecutionMetaData(std::move(meta)); + } + + // if any txns were emitted then add them to the HookEmissions + if (!emission_txnid.empty()) + { + for (auto const& etxnid : emission_txnid) + { + STObject meta { sfHookEmission }; + meta.setFieldH256(sfHookHash, hookResult.hookHash); + meta.setAccountID(sfHookAccount, hookResult.account); + meta.setFieldH256(sfEmittedTxnID, etxnid); + avi.addHookEmissionMetaData(std::move(meta)); + } + } return tesSUCCESS; } diff --git a/src/ripple/app/tx/impl/URIToken.cpp b/src/ripple/app/tx/impl/URIToken.cpp index 46a61cb8a..f2fd39f69 100644 --- a/src/ripple/app/tx/impl/URIToken.cpp +++ b/src/ripple/app/tx/impl/URIToken.cpp @@ -213,6 +213,12 @@ URIToken::preclaim(PreclaimContext const& ctx) if (ctx.view.exists( keylet::uritoken(acc, ctx.tx.getFieldVL(sfURI)))) return tecDUPLICATE; + + // check if destination is specified, and if it is then check if it exists + if (ctx.tx.isFieldPresent(sfDestination) && + !ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfDestination)))) + return tecNO_DST; + return tesSUCCESS; } @@ -370,7 +376,16 @@ URIToken::doApply() return tecDUPLICATE; sleU = std::make_shared(*kl); - sleU->setAccountID(sfOwner, account_); + + AccountID dest = + ctx_.tx.isFieldPresent(sfDestination) + ? ctx_.tx.getAccountID(sfDestination) + : account_; + + if (!view().exists(keylet::account(dest))) + return tecNO_DST; + + sleU->setAccountID(sfOwner, dest); sleU->setAccountID(sfIssuer, account_); sleU->setFieldVL(sfURI, ctx_.tx.getFieldVL(sfURI)); @@ -381,7 +396,7 @@ URIToken::doApply() sleU->setFlag(tfBurnable); auto const page = view().dirInsert( - keylet::ownerDir(account_), *kl, describeOwnerDir(account_)); + keylet::ownerDir(dest), *kl, describeOwnerDir(dest)); JLOG(j_.trace()) << "Adding URIToken to owner directory " << to_string(kl->key) diff --git a/src/ripple/ledger/ApplyViewImpl.h b/src/ripple/ledger/ApplyViewImpl.h index 64de15d51..d4915103d 100644 --- a/src/ripple/ledger/ApplyViewImpl.h +++ b/src/ripple/ledger/ApplyViewImpl.h @@ -79,11 +79,17 @@ public: * Takes ownership / use std::move */ void - addHookMetaData(STObject&& hookExecution) + addHookExecutionMetaData(STObject&& hookExecution) { hookExecution_.push_back(std::move(hookExecution)); } + void + addHookEmissionMetaData(STObject&& hookEmission) + { + hookEmission_.push_back(std::move(hookEmission)); + } + void setHookMetaData(std::vector&& vec) { @@ -122,6 +128,7 @@ public: private: std::optional deliver_; std::vector hookExecution_; + std::vector hookEmission_; }; } // namespace ripple diff --git a/src/ripple/ledger/detail/ApplyStateTable.h b/src/ripple/ledger/detail/ApplyStateTable.h index 3c028fd48..cd3bd92a0 100644 --- a/src/ripple/ledger/detail/ApplyStateTable.h +++ b/src/ripple/ledger/detail/ApplyStateTable.h @@ -74,6 +74,7 @@ public: STTx const& tx, std::optional const& deliver, std::vector const& hookExecution, + std::vector const& hookEmission, beast::Journal j); void @@ -83,6 +84,7 @@ public: TER ter, std::optional const& deliver, std::vector const& hookExecution, + std::vector const& hookEmission, beast::Journal j); bool diff --git a/src/ripple/ledger/impl/ApplyStateTable.cpp b/src/ripple/ledger/impl/ApplyStateTable.cpp index dc83d731f..ea6ebddef 100644 --- a/src/ripple/ledger/impl/ApplyStateTable.cpp +++ b/src/ripple/ledger/impl/ApplyStateTable.cpp @@ -117,6 +117,7 @@ ApplyStateTable::generateTxMeta( STTx const& tx, std::optional const& deliver, std::vector const& hookExecution, + std::vector const& hookEmission, beast::Journal j) { TxMeta meta(tx.getTransactionID(), to.seq()); @@ -126,6 +127,9 @@ ApplyStateTable::generateTxMeta( if (!hookExecution.empty()) meta.setHookExecutions(STArray{hookExecution, sfHookExecutions}); + if (!hookEmission.empty()) + meta.setHookEmissions(STArray{hookEmission, sfHookEmissions}); + Mods newMod; for (auto& item : items_) { @@ -257,6 +261,7 @@ ApplyStateTable::apply( TER ter, std::optional const& deliver, std::vector const& hookExecution, + std::vector const& hookEmission, beast::Journal j) { // Build metadata and insert @@ -268,7 +273,7 @@ ApplyStateTable::apply( // generate meta auto [meta, newMod] = - generateTxMeta(to, tx, deliver, hookExecution, j); + generateTxMeta(to, tx, deliver, hookExecution, hookEmission, j); // add any new modified nodes to the modification set for (auto& mod : newMod) diff --git a/src/ripple/ledger/impl/ApplyViewImpl.cpp b/src/ripple/ledger/impl/ApplyViewImpl.cpp index b76224bb5..152e683fd 100644 --- a/src/ripple/ledger/impl/ApplyViewImpl.cpp +++ b/src/ripple/ledger/impl/ApplyViewImpl.cpp @@ -31,7 +31,7 @@ ApplyViewImpl::ApplyViewImpl(ReadView const* base, ApplyFlags flags) void ApplyViewImpl::apply(OpenView& to, STTx const& tx, TER ter, beast::Journal j) { - items_.apply(to, tx, ter, deliver_, hookExecution_, j); + items_.apply(to, tx, ter, deliver_, hookExecution_, hookEmission_, j); } TxMeta @@ -39,7 +39,7 @@ ApplyViewImpl:: generateProvisionalMeta(OpenView const& to, STTx const& tx, beast::Journal j) { auto [meta, _] = - items_.generateTxMeta(to, tx, deliver_, hookExecution_, j); + items_.generateTxMeta(to, tx, deliver_, hookExecution_, hookEmission_, j); return meta; } diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index f381eaa6d..9047d0deb 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -479,6 +479,7 @@ extern SF_UINT256 const sfEscrowID; extern SF_UINT256 const sfURITokenID; extern SF_UINT256 const sfGovernanceFlags; extern SF_UINT256 const sfGovernanceMarks; +extern SF_UINT256 const sfEmittedTxnID; // currency amount (common) extern SF_AMOUNT const sfAmount; @@ -585,6 +586,7 @@ extern SField const sfHookParameter; extern SField const sfHookGrant; extern SField const sfActiveValidator; extern SField const sfImportVLKey; +extern SField const sfHookEmission; // array of objects (common) // ARRAY/1 is reserved for end of array @@ -611,6 +613,7 @@ extern SField const sfHookGrants; extern SField const sfGenesisMints; extern SField const sfActiveValidators; extern SField const sfImportVLKeys; +extern SField const sfHookEmissions; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/TxMeta.h b/src/ripple/protocol/TxMeta.h index f847a3961..82fd62faa 100644 --- a/src/ripple/protocol/TxMeta.h +++ b/src/ripple/protocol/TxMeta.h @@ -122,18 +122,36 @@ public: return *mHookExecutions; } + STArray const& + getHookEmissions() const + { + return *mHookEmissions; + } + void setHookExecutions(STArray const& hookExecutions) { mHookExecutions = hookExecutions; } + void + setHookEmissions(STArray const& hookEmissions) + { + mHookEmissions = hookEmissions; + } + bool hasHookExecutions() const { return static_cast(mHookExecutions); } + bool + hasHookEmissions() const + { + return static_cast(mHookEmissions); + } + STAmount getDeliveredAmount() const { @@ -155,6 +173,7 @@ private: std::optional mDelivered; std::optional mHookExecutions; + std::optional mHookEmissions; STArray mNodes; }; diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index c08708280..05ad7695b 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -78,6 +78,14 @@ InnerObjectFormats::InnerObjectFormats() {sfHookEmitCount, soeREQUIRED} }); + add(sfHookEmission.jsonName.c_str(), + sfHookEmission.getCode(), + { + {sfHookHash, soeREQUIRED}, + {sfHookAccount, soeREQUIRED}, + {sfEmittedTxnID, soeREQUIRED} + }); + add(sfHookDefinition.jsonName.c_str(), sfHookDefinition.getCode(), { diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index c0b1e09a1..87ba614ba 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -232,6 +232,7 @@ CONSTRUCT_TYPED_SFIELD(sfEscrowID, "EscrowID", UINT256, CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256, 36); CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99); CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98); +CONSTRUCT_TYPED_SFIELD(sfEmittedTxnID, "EmittedTxnID", UINT256, 97); // currency amount (common) CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1); @@ -341,6 +342,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT, CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT, 96); CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95); CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94); +CONSTRUCT_UNTYPED_SFIELD(sfHookEmission, "HookEmission", OBJECT, 93); // array of objects // ARRAY/1 is reserved for end of array @@ -361,6 +363,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY, CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18); CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19); CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20); +CONSTRUCT_UNTYPED_SFIELD(sfHookEmissions, "HookEmissions", ARRAY, 95); CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY, 96); CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95); CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94); diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 2f3edda04..1a8e096fb 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -398,6 +398,7 @@ TxFormats::TxFormats() { {sfURI, soeREQUIRED}, {sfDigest, soeOPTIONAL}, + {sfDestination, soeOPTIONAL}, {sfTicketSequence, soeOPTIONAL}, }, commonFields); diff --git a/src/ripple/protocol/impl/TxMeta.cpp b/src/ripple/protocol/impl/TxMeta.cpp index bc515316c..706aaaa49 100644 --- a/src/ripple/protocol/impl/TxMeta.cpp +++ b/src/ripple/protocol/impl/TxMeta.cpp @@ -225,6 +225,9 @@ TxMeta::getAsObject() const if (hasHookExecutions()) metaData.setFieldArray(sfHookExecutions, getHookExecutions()); + if (hasHookEmissions()) + metaData.setFieldArray(sfHookEmissions, getHookEmissions()); + return metaData; }