From a07a729e3dee07b9c845d9dab67be4bc4a4eb3ac Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Mon, 31 Jan 2022 10:23:21 +0000 Subject: [PATCH] Reserve field codes for Hooks: In order to preserve the Hooks ABI, it is important that field values used for hooks be stable going forward. This commit reserves the required codes so that they will not be repurposed before Hooks can be proposed for inclusion in the codebase. --- Builds/CMake/RippledCore.cmake | 1 + src/ripple/protocol/SField.h | 39 ++++++ src/ripple/protocol/impl/SField.cpp | 38 ++++++ src/test/protocol/Hooks_test.cpp | 197 ++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 src/test/protocol/Hooks_test.cpp diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index e0c627e2bd..cb6e03eeda 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -891,6 +891,7 @@ if (tests) src/test/protocol/InnerObjectFormats_test.cpp src/test/protocol/Issue_test.cpp src/test/protocol/KnownFormatToGRPC_test.cpp + src/test/protocol/Hooks_test.cpp src/test/protocol/PublicKey_test.cpp src/test/protocol/Quality_test.cpp src/test/protocol/STAccount_test.cpp diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index f9278ea73f..28da73436b 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -341,6 +341,7 @@ extern SF_UINT8 const sfMethod; extern SF_UINT8 const sfTransactionResult; extern SF_UINT8 const sfTickSize; extern SF_UINT8 const sfUNLModifyDisabling; +extern SF_UINT8 const sfHookResult; // 16-bit integers extern SF_UINT16 const sfLedgerEntryType; @@ -349,6 +350,10 @@ extern SF_UINT16 const sfSignerWeight; // 16-bit integers (uncommon) extern SF_UINT16 const sfVersion; +extern SF_UINT16 const sfHookStateChangeCount; +extern SF_UINT16 const sfHookEmitCount; +extern SF_UINT16 const sfHookExecutionIndex; +extern SF_UINT16 const sfHookApiVersion; // 32-bit integers (common) extern SF_UINT32 const sfFlags; @@ -392,6 +397,8 @@ extern SF_UINT32 const sfSignerListID; extern SF_UINT32 const sfSettleDelay; extern SF_UINT32 const sfTicketCount; extern SF_UINT32 const sfTicketSequence; +extern SF_UINT32 const sfHookStateCount; +extern SF_UINT32 const sfEmitGeneration; // 64-bit integers extern SF_UINT64 const sfIndexNext; @@ -405,6 +412,11 @@ extern SF_UINT64 const sfHighNode; extern SF_UINT64 const sfDestinationNode; extern SF_UINT64 const sfCookie; extern SF_UINT64 const sfServerVersion; +extern SF_UINT64 const sfHookOn; +extern SF_UINT64 const sfHookInstructionCount; +extern SF_UINT64 const sfEmitBurden; +extern SF_UINT64 const sfHookReturnCode; +extern SF_UINT64 const sfReferenceCount; // 128-bit extern SF_HASH128 const sfEmailHash; @@ -425,6 +437,9 @@ extern SF_HASH256 const sfLedgerIndex; extern SF_HASH256 const sfWalletLocator; extern SF_HASH256 const sfRootIndex; extern SF_HASH256 const sfAccountTxnID; +extern SF_HASH256 const sfEmitParentTxnID; +extern SF_HASH256 const sfEmitNonce; +extern SF_HASH256 const sfEmitHookHash; // 256-bit (uncommon) extern SF_HASH256 const sfBookDirectory; @@ -436,6 +451,10 @@ extern SF_HASH256 const sfChannel; extern SF_HASH256 const sfConsensusHash; extern SF_HASH256 const sfCheckID; extern SF_HASH256 const sfValidatedHash; +extern SF_HASH256 const sfHookStateKey; +extern SF_HASH256 const sfHookHash; +extern SF_HASH256 const sfHookNamespace; +extern SF_HASH256 const sfHookSetTxnID; // currency amount (common) extern SF_AMOUNT const sfAmount; @@ -476,6 +495,10 @@ extern SF_VL const sfMasterSignature; extern SF_VL const sfUNLModifyValidator; extern SF_VL const sfValidatorToDisable; extern SF_VL const sfValidatorToReEnable; +extern SF_VL const sfHookStateData; +extern SF_VL const sfHookReturnString; +extern SF_VL const sfHookParameterName; +extern SF_VL const sfHookParameterValue; // account extern SF_ACCOUNT const sfAccount; @@ -486,6 +509,10 @@ extern SF_ACCOUNT const sfAuthorize; extern SF_ACCOUNT const sfUnauthorize; extern SF_ACCOUNT const sfTarget; extern SF_ACCOUNT const sfRegularKey; +extern SF_ACCOUNT const sfEmitCallback; + +// account (uncommon) +extern SF_ACCOUNT const sfHookAccount; // path set extern SField const sfPaths; @@ -510,6 +537,11 @@ extern SField const sfSignerEntry; extern SField const sfSigner; extern SField const sfMajority; extern SField const sfDisabledValidator; +extern SField const sfEmittedTxn; +extern SField const sfHook; +extern SField const sfHookDefinition; +extern SField const sfHookParameter; +extern SField const sfHookGrant; // array of objects // ARRAY/1 is reserved for end of array @@ -523,6 +555,13 @@ extern SField const sfAffectedNodes; extern SField const sfMemos; extern SField const sfMajorities; extern SField const sfDisabledValidators; +extern SField const sfEmitDetails; +extern SField const sfHookExecutions; +extern SField const sfHookExecution; +extern SField const sfHookParameters; +extern SField const sfHooks; +extern SField const sfHookGrants; + //------------------------------------------------------------------------------ } // namespace ripple diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 590ffeb65a..679248dea6 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -88,6 +88,7 @@ CONSTRUCT_TYPED_SFIELD(sfTransactionResult, "TransactionResult", UINT8, // 8-bit integers (uncommon) CONSTRUCT_TYPED_SFIELD(sfTickSize, "TickSize", UINT8, 16); CONSTRUCT_TYPED_SFIELD(sfUNLModifyDisabling, "UNLModifyDisabling", UINT8, 17); +CONSTRUCT_TYPED_SFIELD(sfHookResult, "HookResult", UINT8, 18); // 16-bit integers CONSTRUCT_TYPED_SFIELD(sfLedgerEntryType, "LedgerEntryType", UINT16, 1, SField::sMD_Never); @@ -96,6 +97,10 @@ CONSTRUCT_TYPED_SFIELD(sfSignerWeight, "SignerWeight", UINT16, // 16-bit integers (uncommon) CONSTRUCT_TYPED_SFIELD(sfVersion, "Version", UINT16, 16); +CONSTRUCT_TYPED_SFIELD(sfHookStateChangeCount, "HookStateChangeCount", UINT16, 17); +CONSTRUCT_TYPED_SFIELD(sfHookEmitCount, "HookEmitCount", UINT16, 18); +CONSTRUCT_TYPED_SFIELD(sfHookExecutionIndex, "HookExecutionIndex", UINT16, 19); +CONSTRUCT_TYPED_SFIELD(sfHookApiVersion, "HookApiVersion", UINT16, 20); // 32-bit integers (common) CONSTRUCT_TYPED_SFIELD(sfFlags, "Flags", UINT32, 2); @@ -139,6 +144,8 @@ CONSTRUCT_TYPED_SFIELD(sfSignerListID, "SignerListID", UINT32, CONSTRUCT_TYPED_SFIELD(sfSettleDelay, "SettleDelay", UINT32, 39); CONSTRUCT_TYPED_SFIELD(sfTicketCount, "TicketCount", UINT32, 40); CONSTRUCT_TYPED_SFIELD(sfTicketSequence, "TicketSequence", UINT32, 41); +CONSTRUCT_TYPED_SFIELD(sfHookStateCount, "HookStateCount", UINT32, 45); +CONSTRUCT_TYPED_SFIELD(sfEmitGeneration, "EmitGeneration", UINT32, 46); // 64-bit integers CONSTRUCT_TYPED_SFIELD(sfIndexNext, "IndexNext", UINT64, 1); @@ -152,6 +159,11 @@ CONSTRUCT_TYPED_SFIELD(sfHighNode, "HighNode", UINT64, CONSTRUCT_TYPED_SFIELD(sfDestinationNode, "DestinationNode", UINT64, 9); CONSTRUCT_TYPED_SFIELD(sfCookie, "Cookie", UINT64, 10); CONSTRUCT_TYPED_SFIELD(sfServerVersion, "ServerVersion", UINT64, 11); +CONSTRUCT_TYPED_SFIELD(sfEmitBurden, "EmitBurden", UINT64, 13); +CONSTRUCT_TYPED_SFIELD(sfHookOn, "HookOn", UINT64, 16); +CONSTRUCT_TYPED_SFIELD(sfHookInstructionCount, "HookInstructionCount", UINT64, 17); +CONSTRUCT_TYPED_SFIELD(sfHookReturnCode, "HookReturnCode", UINT64, 18); +CONSTRUCT_TYPED_SFIELD(sfReferenceCount, "ReferenceCount", UINT64, 19); // 128-bit CONSTRUCT_TYPED_SFIELD(sfEmailHash, "EmailHash", HASH128, 1); @@ -172,6 +184,9 @@ CONSTRUCT_TYPED_SFIELD(sfLedgerIndex, "LedgerIndex", HASH256, CONSTRUCT_TYPED_SFIELD(sfWalletLocator, "WalletLocator", HASH256, 7); CONSTRUCT_TYPED_SFIELD(sfRootIndex, "RootIndex", HASH256, 8, SField::sMD_Always); CONSTRUCT_TYPED_SFIELD(sfAccountTxnID, "AccountTxnID", HASH256, 9); +CONSTRUCT_TYPED_SFIELD(sfEmitParentTxnID, "EmitParentTxnID", HASH256, 11); +CONSTRUCT_TYPED_SFIELD(sfEmitNonce, "EmitNonce", HASH256, 12); +CONSTRUCT_TYPED_SFIELD(sfEmitHookHash, "EmitHookHash", HASH256, 13); // 256-bit (uncommon) CONSTRUCT_TYPED_SFIELD(sfBookDirectory, "BookDirectory", HASH256, 16); @@ -184,6 +199,10 @@ CONSTRUCT_TYPED_SFIELD(sfChannel, "Channel", HASH256, CONSTRUCT_TYPED_SFIELD(sfConsensusHash, "ConsensusHash", HASH256, 23); CONSTRUCT_TYPED_SFIELD(sfCheckID, "CheckID", HASH256, 24); CONSTRUCT_TYPED_SFIELD(sfValidatedHash, "ValidatedHash", HASH256, 25); +CONSTRUCT_TYPED_SFIELD(sfHookStateKey, "HookStateKey", HASH256, 30); +CONSTRUCT_TYPED_SFIELD(sfHookHash, "HookHash", HASH256, 31); +CONSTRUCT_TYPED_SFIELD(sfHookNamespace, "HookNamespace", HASH256, 32); +CONSTRUCT_TYPED_SFIELD(sfHookSetTxnID, "HookSetTxnID", HASH256, 33); // currency amount (common) CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1); @@ -225,6 +244,10 @@ CONSTRUCT_TYPED_SFIELD(sfMasterSignature, "MasterSignature", VL, CONSTRUCT_TYPED_SFIELD(sfUNLModifyValidator, "UNLModifyValidator", VL, 19); CONSTRUCT_TYPED_SFIELD(sfValidatorToDisable, "ValidatorToDisable", VL, 20); CONSTRUCT_TYPED_SFIELD(sfValidatorToReEnable, "ValidatorToReEnable", VL, 21); +CONSTRUCT_TYPED_SFIELD(sfHookStateData, "HookStateData", VL, 22); +CONSTRUCT_TYPED_SFIELD(sfHookReturnString, "HookReturnString", VL, 23); +CONSTRUCT_TYPED_SFIELD(sfHookParameterName, "HookParameterName", VL, 24); +CONSTRUCT_TYPED_SFIELD(sfHookParameterValue, "HookParameterValue", VL, 25); // account CONSTRUCT_TYPED_SFIELD(sfAccount, "Account", ACCOUNT, 1); @@ -235,6 +258,10 @@ CONSTRUCT_TYPED_SFIELD(sfAuthorize, "Authorize", ACCOUNT, CONSTRUCT_TYPED_SFIELD(sfUnauthorize, "Unauthorize", ACCOUNT, 6); // 7 is currently unused CONSTRUCT_TYPED_SFIELD(sfRegularKey, "RegularKey", ACCOUNT, 8); +CONSTRUCT_TYPED_SFIELD(sfEmitCallback, "EmitCallback", ACCOUNT, 10); + +// account (uncommon) +CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16); // vector of 256-bit CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never); @@ -256,12 +283,19 @@ CONSTRUCT_UNTYPED_SFIELD(sfNewFields, "NewFields", OBJECT, CONSTRUCT_UNTYPED_SFIELD(sfTemplateEntry, "TemplateEntry", OBJECT, 9); CONSTRUCT_UNTYPED_SFIELD(sfMemo, "Memo", OBJECT, 10); CONSTRUCT_UNTYPED_SFIELD(sfSignerEntry, "SignerEntry", OBJECT, 11); +CONSTRUCT_UNTYPED_SFIELD(sfEmitDetails, "EmitDetails", OBJECT, 13); +CONSTRUCT_UNTYPED_SFIELD(sfHook, "Hook", OBJECT, 14); // inner object (uncommon) CONSTRUCT_UNTYPED_SFIELD(sfSigner, "Signer", OBJECT, 16); // 17 has not been used yet CONSTRUCT_UNTYPED_SFIELD(sfMajority, "Majority", OBJECT, 18); CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidator, "DisabledValidator", OBJECT, 19); +CONSTRUCT_UNTYPED_SFIELD(sfEmittedTxn, "EmittedTxn", OBJECT, 20); +CONSTRUCT_UNTYPED_SFIELD(sfHookExecution, "HookExecution", OBJECT, 21); +CONSTRUCT_UNTYPED_SFIELD(sfHookDefinition, "HookDefinition", OBJECT, 22); +CONSTRUCT_UNTYPED_SFIELD(sfHookParameter, "HookParameter", OBJECT, 23); +CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT, 24); // array of objects // ARRAY/1 is reserved for end of array @@ -273,10 +307,14 @@ CONSTRUCT_UNTYPED_SFIELD(sfNecessary, "Necessary", ARRAY, CONSTRUCT_UNTYPED_SFIELD(sfSufficient, "Sufficient", ARRAY, 7); CONSTRUCT_UNTYPED_SFIELD(sfAffectedNodes, "AffectedNodes", ARRAY, 8); CONSTRUCT_UNTYPED_SFIELD(sfMemos, "Memos", ARRAY, 9); +CONSTRUCT_UNTYPED_SFIELD(sfHooks, "Hooks", ARRAY, 11); // array of objects (uncommon) CONSTRUCT_UNTYPED_SFIELD(sfMajorities, "Majorities", ARRAY, 16); CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY, 17); +CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18); +CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19); +CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20); // clang-format on diff --git a/src/test/protocol/Hooks_test.cpp b/src/test/protocol/Hooks_test.cpp new file mode 100644 index 0000000000..161404195a --- /dev/null +++ b/src/test/protocol/Hooks_test.cpp @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2017 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include + +namespace ripple { + +class Hooks_test : public beast::unit_test::suite +{ + /** + * This unit test was requested here: + * https://github.com/ripple/rippled/pull/4089#issuecomment-1050274539 + * These are tests that exercise facilities that are reserved for when Hooks + * is merged in the future. + **/ + + void + testHookFields() + { + testcase("Test Hooks fields"); + + using namespace test::jtx; + + std::vector> fields_to_test = { + sfHookResult, + sfHookStateChangeCount, + sfHookEmitCount, + sfHookExecutionIndex, + sfHookApiVersion, + sfHookStateCount, + sfEmitGeneration, + sfHookOn, + sfHookInstructionCount, + sfEmitBurden, + sfHookReturnCode, + sfReferenceCount, + sfEmitParentTxnID, + sfEmitNonce, + sfEmitHookHash, + sfHookStateKey, + sfHookHash, + sfHookNamespace, + sfHookSetTxnID, + sfHookStateData, + sfHookReturnString, + sfHookParameterName, + sfHookParameterValue, + sfEmitCallback, + sfHookAccount, + sfEmittedTxn, + sfHook, + sfHookDefinition, + sfHookParameter, + sfHookGrant, + sfEmitDetails, + sfHookExecutions, + sfHookExecution, + sfHookParameters, + sfHooks, + sfHookGrants}; + + for (auto const& rf : fields_to_test) + { + SField const& f = rf.get(); + + STObject dummy{sfGeneric}; + + BEAST_EXPECT(!dummy.isFieldPresent(f)); + + switch (f.fieldType) + { + case STI_UINT8: { + dummy.setFieldU8(f, 0); + BEAST_EXPECT(dummy.getFieldU8(f) == 0); + + dummy.setFieldU8(f, 255); + BEAST_EXPECT(dummy.getFieldU8(f) == 255); + + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_UINT16: { + dummy.setFieldU16(f, 0); + BEAST_EXPECT(dummy.getFieldU16(f) == 0); + + dummy.setFieldU16(f, 0xFFFFU); + BEAST_EXPECT(dummy.getFieldU16(f) == 0xFFFFU); + + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_UINT32: { + dummy.setFieldU32(f, 0); + BEAST_EXPECT(dummy.getFieldU32(f) == 0); + + dummy.setFieldU32(f, 0xFFFFFFFFU); + BEAST_EXPECT(dummy.getFieldU32(f) == 0xFFFFFFFFU); + + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_UINT64: { + dummy.setFieldU64(f, 0); + BEAST_EXPECT(dummy.getFieldU64(f) == 0); + + dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU); + BEAST_EXPECT(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU); + + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_HASH256: { + uint256 u = uint256::fromVoid( + "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE" + "EFDEADBEEF"); + dummy.setFieldH256(f, u); + BEAST_EXPECT(dummy.getFieldH256(f) == u); + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_VL: { + std::vector v{1, 2, 3}; + dummy.setFieldVL(f, v); + BEAST_EXPECT(dummy.getFieldVL(f) == v); + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_ACCOUNT: { + AccountID id = *parseBase58( + "rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT"); + dummy.setAccountID(f, id); + BEAST_EXPECT(dummy.getAccountID(f) == id); + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_OBJECT: { + dummy.emplace_back(STObject{f}); + BEAST_EXPECT(dummy.getField(f).getFName() == f); + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + case STI_ARRAY: { + STArray dummy2{f, 2}; + dummy2.push_back(STObject{sfGeneric}); + dummy2.push_back(STObject{sfGeneric}); + dummy.setFieldArray(f, dummy2); + BEAST_EXPECT(dummy.getFieldArray(f) == dummy2); + BEAST_EXPECT(dummy.isFieldPresent(f)); + break; + } + + default: + BEAST_EXPECT(false); + } + } + } + +public: + void + run() override + { + using namespace test::jtx; + testHookFields(); + } +}; + +BEAST_DEFINE_TESTSUITE(Hooks, protocol, ripple); + +} // namespace ripple