mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-24 15:40:08 +00:00
export hook apis, untested
This commit is contained in:
@@ -350,7 +350,10 @@ enum hook_return_code : int64_t {
|
||||
MEM_OVERLAP = -43, // one or more specified buffers are the same memory
|
||||
TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state
|
||||
// entires in the combined hook chains
|
||||
TOO_MANY_NAMESPACES = -45
|
||||
TOO_MANY_NAMESPACES = -45,
|
||||
EXPORT_FAILURE = -46,
|
||||
TOO_MANY_EXPORTED_TXN = -47,
|
||||
|
||||
};
|
||||
|
||||
enum ExitType : uint8_t {
|
||||
@@ -364,6 +367,7 @@ const uint16_t max_state_modifications = 256;
|
||||
const uint8_t max_slots = 255;
|
||||
const uint8_t max_nonce = 255;
|
||||
const uint8_t max_emit = 255;
|
||||
const uint6_t max_export = 4;
|
||||
const uint8_t max_params = 16;
|
||||
const double fee_base_multiplier = 1.1f;
|
||||
|
||||
@@ -469,6 +473,13 @@ static const APIWhitelist import_whitelist_1{
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
static const APIWhitelist import_whitelist_2{
|
||||
// clang-format off
|
||||
HOOK_API_DEFINITION(I64, export, (I32, I32)),
|
||||
HOOK_API_DEFINITION(I64, export_reserve, (I32)),
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#undef HOOK_API_DEFINITION
|
||||
#undef I32
|
||||
#undef I64
|
||||
|
||||
@@ -1034,6 +1034,12 @@ validateGuards(
|
||||
{
|
||||
// PASS, this is a version 1 api
|
||||
}
|
||||
else if (rulesVersion & 0x04U &&
|
||||
hook_api::import_whitelist_2.find(import_name) !=
|
||||
hook_api::import_whitelist_2.end())
|
||||
{
|
||||
// PASS, this is an export api
|
||||
}
|
||||
else
|
||||
{
|
||||
GUARDLOG(hook::log::IMPORT_ILLEGAL)
|
||||
|
||||
@@ -406,6 +406,11 @@ DECLARE_HOOK_FUNCTION(
|
||||
uint32_t slot_no_tx,
|
||||
uint32_t slot_no_meta);
|
||||
|
||||
DECLARE_HOOK_FUNCTION(
|
||||
int64_t,
|
||||
export,
|
||||
uint32_t read_ptr,
|
||||
uint32_t read_len);
|
||||
/*
|
||||
DECLARE_HOOK_FUNCTION(int64_t, str_find, uint32_t hread_ptr,
|
||||
uint32_t hread_len, uint32_t nread_ptr, uint32_t nread_len, uint32_t mode,
|
||||
@@ -485,6 +490,8 @@ struct HookResult
|
||||
|
||||
std::queue<std::shared_ptr<ripple::Transaction>>
|
||||
emittedTxn{}; // etx stored here until accept/rollback
|
||||
std::queue<std::shared_ptr<ripple::Transaction>>
|
||||
exportedTxn{};
|
||||
HookStateMap& stateMap;
|
||||
uint16_t changedStateCount = 0;
|
||||
std::map<
|
||||
@@ -541,6 +548,7 @@ struct HookContext
|
||||
uint16_t ledger_nonce_counter{0};
|
||||
int64_t expected_etxn_count{-1}; // make this a 64bit int so the uint32
|
||||
// from the hookapi cant overflow it
|
||||
int64_t expected_export_count{-1};
|
||||
std::map<ripple::uint256, bool> nonce_used{};
|
||||
uint32_t generation =
|
||||
0; // used for caching, only generated when txn_generation is called
|
||||
@@ -877,6 +885,9 @@ public:
|
||||
ADD_HOOK_FUNCTION(meta_slot, ctx);
|
||||
ADD_HOOK_FUNCTION(xpop_slot, ctx);
|
||||
|
||||
ADD_HOOK_FUNCTION(export, ctx);
|
||||
ADD_HOOK_FUNCTION(export_reserve, ctx);
|
||||
|
||||
/*
|
||||
ADD_HOOK_FUNCTION(str_find, ctx);
|
||||
ADD_HOOK_FUNCTION(str_replace, ctx);
|
||||
|
||||
@@ -79,7 +79,7 @@ main(int argc, char** argv)
|
||||
|
||||
close(fd);
|
||||
|
||||
auto result = validateGuards(hook, std::cout, "", 3);
|
||||
auto result = validateGuards(hook, std::cout, "", 7);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
|
||||
@@ -1971,6 +1971,8 @@ hook::finalizeHookResult(
|
||||
// directory) if we are allowed to
|
||||
std::vector<std::pair<uint256 /* txnid */, uint256 /* emit nonce */>>
|
||||
emission_txnid;
|
||||
std::vector<uint256 /* txnid */>
|
||||
exported_txnid;
|
||||
|
||||
if (doEmit)
|
||||
{
|
||||
@@ -2026,6 +2028,58 @@ hook::finalizeHookResult(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DBG_PRINTF("exported txn count: %d\n", hookResult.exportedTxn.size());
|
||||
for (; hookResult.exportedTxn.size() > 0; hookResult.exportedTxn.pop())
|
||||
{
|
||||
auto& tpTrans = hookResult.exportedTxn.front();
|
||||
auto& id = tpTrans->getID();
|
||||
JLOG(j.trace()) << "HookExport[" << HR_ACC() << "]: " << id;
|
||||
|
||||
// exported txns must be marked bad by the hash router to ensure under
|
||||
// no circumstances they will enter consensus on *this* chain.
|
||||
applyCtx.app.getHashRouter().setFlags(id, SF_BAD);
|
||||
|
||||
std::shared_ptr<const ripple::STTx> ptr =
|
||||
tpTrans->getSTransaction();
|
||||
|
||||
auto exportedId = keylet::exportedTxn(id);
|
||||
auto sleExported = applyCtx.view().peek(exportedId);
|
||||
|
||||
if (!sleExported)
|
||||
{
|
||||
exported_txnid.emplace_back(id);
|
||||
|
||||
sleExported = std::make_shared<SLE>(exportedId);
|
||||
|
||||
// RH TODO: add a new constructor to STObject to avoid this
|
||||
// serder thing
|
||||
ripple::Serializer s;
|
||||
ptr->add(s);
|
||||
SerialIter sit(s.slice());
|
||||
|
||||
sleExported->emplace_back(ripple::STObject(sit, sfExportedTxn));
|
||||
auto page = applyCtx.view().dirInsert(
|
||||
keylet::exportedDir(), exportedId, [&](SLE::ref sle) {
|
||||
(*sle)[sfFlags] = lsfEmittedDir;
|
||||
});
|
||||
|
||||
if (page)
|
||||
{
|
||||
(*sleExported)[sfOwnerNode] = *page;
|
||||
applyCtx.view().insert(sleExported);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.warn())
|
||||
<< "HookError[" << HR_ACC() << "]: "
|
||||
<< "Export Directory full when trying to insert "
|
||||
<< id;
|
||||
return tecDIR_FULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool const fixV2 = applyCtx.view().rules().enabled(fixXahauV2);
|
||||
@@ -2052,6 +2106,12 @@ hook::finalizeHookResult(
|
||||
meta.setFieldU16(
|
||||
sfHookEmitCount,
|
||||
emission_txnid.size()); // this will never wrap, hard limit
|
||||
if (applyCtx.view().rules().enabled(featureExport))
|
||||
{
|
||||
meta.setFieldU16(
|
||||
sfHookExportCount,
|
||||
exported_txnid.size());
|
||||
}
|
||||
meta.setFieldU16(sfHookExecutionIndex, exec_index);
|
||||
meta.setFieldU16(sfHookStateChangeCount, hookResult.changedStateCount);
|
||||
meta.setFieldH256(sfHookHash, hookResult.hookHash);
|
||||
@@ -3888,6 +3948,27 @@ DEFINE_HOOK_FUNCTION(int64_t, etxn_reserve, uint32_t count)
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
|
||||
DEFINE_HOOK_FUNCTION(int64_t, export_reserve, uint32_t count)
|
||||
{
|
||||
HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx,
|
||||
// hookCtx on current stack
|
||||
|
||||
if (hookCtx.expected_export_count > -1)
|
||||
return ALREADY_SET;
|
||||
|
||||
if (count < 1)
|
||||
return TOO_SMALL;
|
||||
|
||||
if (count > hook_api::max_export)
|
||||
return TOO_BIG;
|
||||
|
||||
hookCtx.expected_export_count = count;
|
||||
|
||||
return count;
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
|
||||
// Compute the burden of an emitted transaction based on a number of factors
|
||||
DEFINE_HOOK_FUNCNARG(int64_t, etxn_burden)
|
||||
{
|
||||
@@ -6156,6 +6237,92 @@ DEFINE_HOOK_FUNCTION(
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
|
||||
DEFINE_HOOK_FUNCTION(
|
||||
int64_t,
|
||||
export,
|
||||
uint32_t read_ptr,
|
||||
uint32_t read_len,
|
||||
uint32_t write_ptr,
|
||||
uint32_t write_len);
|
||||
{
|
||||
HOOK_SETUP();
|
||||
|
||||
if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (write_len < 32)
|
||||
return TOO_SMALL;
|
||||
|
||||
auto& app = hookCtx.applyCtx.app;
|
||||
|
||||
if (hookCtx.expected_export_count < 0)
|
||||
return PREREQUISITE_NOT_MET;
|
||||
|
||||
if (hookCtx.result.exportedTxn.size() >= hookCtx.expected_export_count)
|
||||
return TOO_MANY_EXPORTED_TXN;
|
||||
|
||||
ripple::Blob blob{memory + read_ptr, memory + read_ptr + read_len};
|
||||
|
||||
std::shared_ptr<STTx const> stpTrans;
|
||||
try
|
||||
{
|
||||
stpTrans = std::make_shared<STTx const>(
|
||||
SerialIter{memory + read_ptr, read_len});
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
JLOG(j.trace()) << "HookExport[" << HC_ACC() << "]: Failed " << e.what()
|
||||
<< "\n";
|
||||
return EXPORT_FAILURE;
|
||||
}
|
||||
|
||||
if (!stpTrans->isFieldPresent(sfAccount) ||
|
||||
stpTrans->getAccountID(sfAccount) != hookCtx.result.account)
|
||||
{
|
||||
JLOG(j.trace()) << "HookExport[" << HC_ACC()
|
||||
<< "]: Attempted to export a txn that's not for this Hook's Account ID.";
|
||||
return EXPORT_FAILURE;
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
auto tpTrans = std::make_shared<Transaction>(stpTrans, reason, app);
|
||||
// RHTODO: is this needed or wise? VVV
|
||||
if (tpTrans->getStatus() != NEW)
|
||||
{
|
||||
JLOG(j.trace()) << "HookExport[" << HC_ACC()
|
||||
<< "]: tpTrans->getStatus() != NEW";
|
||||
return EXPORT_FAILURE;
|
||||
}
|
||||
auto const& txID = tpTrans->getID();
|
||||
|
||||
if (txID.size() > write_len)
|
||||
return TOO_SMALL;
|
||||
|
||||
if (NOT_IN_BOUNDS(write_ptr, txID.size(), memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
auto const write_txid = [&]() -> int64_t {
|
||||
WRITE_WASM_MEMORY_AND_RETURN(
|
||||
write_ptr,
|
||||
txID.size(),
|
||||
txID.data(),
|
||||
txID.size(),
|
||||
memory,
|
||||
memory_length);
|
||||
};
|
||||
|
||||
int64_t result = write_txid();
|
||||
|
||||
if (result == 32)
|
||||
hookCtx.result.exportedTxn.push(tpTrans);
|
||||
|
||||
return result;
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
/*
|
||||
|
||||
DEFINE_HOOK_FUNCTION(
|
||||
|
||||
@@ -606,7 +606,8 @@ Change::activateXahauGenesis()
|
||||
loggerStream,
|
||||
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
(ctx_.view().rules().enabled(featureHooksUpdate1) ? 1 : 0) +
|
||||
(ctx_.view().rules().enabled(fix20250131) ? 2 : 0));
|
||||
(ctx_.view().rules().enabled(fix20250131) ? 2 : 0) +
|
||||
(ctx_.view().rules().enabled(featureExport) ? 4 : 0));
|
||||
|
||||
if (!result)
|
||||
{
|
||||
|
||||
@@ -491,7 +491,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
|
||||
logger,
|
||||
hsacc,
|
||||
(ctx.rules.enabled(featureHooksUpdate1) ? 1 : 0) +
|
||||
(ctx.rules.enabled(fix20250131) ? 2 : 0));
|
||||
(ctx.rules.enabled(fix20250131) ? 2 : 0) +
|
||||
(ctx.rules.enabled(featureExport) ? 4 : 0));
|
||||
|
||||
if (ctx.j.trace())
|
||||
{
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace detail {
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||
// the actual number of amendments. A LogicError on startup will verify this.
|
||||
static constexpr std::size_t numFeatures = 90;
|
||||
static constexpr std::size_t numFeatures = 91;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -378,6 +378,7 @@ extern uint256 const fixInvalidTxFlags;
|
||||
extern uint256 const featureExtendedHookState;
|
||||
extern uint256 const fixCronStacking;
|
||||
extern uint256 const fixHookAPI20251128;
|
||||
extern uint256 const featureExport;
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -56,9 +56,15 @@ namespace keylet {
|
||||
Keylet const&
|
||||
emittedDir() noexcept;
|
||||
|
||||
Keylet const&
|
||||
exportedDir() noexcept;
|
||||
|
||||
Keylet
|
||||
emittedTxn(uint256 const& id) noexcept;
|
||||
|
||||
Keylet
|
||||
exportedTxn(uint256 const& id) noexcept;
|
||||
|
||||
Keylet
|
||||
hookDefinition(uint256 const& hash) noexcept;
|
||||
|
||||
|
||||
@@ -260,6 +260,8 @@ enum LedgerEntryType : std::uint16_t
|
||||
\sa keylet::emitted
|
||||
*/
|
||||
ltEMITTED_TXN = 'E',
|
||||
|
||||
ltEXPORTED_TXN = 0x4578, // Ex (exported transaction)
|
||||
};
|
||||
// clang-format off
|
||||
|
||||
@@ -318,7 +320,8 @@ enum LedgerSpecificFlags {
|
||||
// ltDIR_NODE
|
||||
lsfNFTokenBuyOffers = 0x00000001,
|
||||
lsfNFTokenSellOffers = 0x00000002,
|
||||
lsfEmittedDir = 0x00000004,
|
||||
lsfEmittedDir = 0x00000004,
|
||||
lsfExportedDir = 0x00000008,
|
||||
|
||||
// ltNFTOKEN_OFFER
|
||||
lsfSellNFToken = 0x00000001,
|
||||
|
||||
@@ -355,6 +355,7 @@ extern SF_UINT16 const sfHookEmitCount;
|
||||
extern SF_UINT16 const sfHookExecutionIndex;
|
||||
extern SF_UINT16 const sfHookApiVersion;
|
||||
extern SF_UINT16 const sfHookStateScale;
|
||||
extern SF_UINT16 const sfHookExportCount;
|
||||
|
||||
// 32-bit integers (common)
|
||||
extern SF_UINT32 const sfNetworkID;
|
||||
|
||||
@@ -484,6 +484,7 @@ REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FIX (fixCronStacking, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FIX (fixHookAPI20251128, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(Export, Supported::yes, VoteBehavior::DefaultNo);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -66,6 +66,8 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
HOOK_DEFINITION = 'D',
|
||||
EMITTED_TXN = 'E',
|
||||
EMITTED_DIR = 'F',
|
||||
EXPORTED_TXN = 0x4578, // Ex
|
||||
EXPORTED_DIR = 0x4564, // Ed
|
||||
NFTOKEN_OFFER = 'q',
|
||||
NFTOKEN_BUY_OFFERS = 'h',
|
||||
NFTOKEN_SELL_OFFERS = 'i',
|
||||
@@ -147,6 +149,14 @@ emittedDir() noexcept
|
||||
return ret;
|
||||
}
|
||||
|
||||
Keylet const&
|
||||
exportedDir() noexcept
|
||||
{
|
||||
static Keylet const ret{
|
||||
ltDIR_NODE, indexHash(LedgerNameSpace::EXPORTED_DIR)};
|
||||
return ret;
|
||||
}
|
||||
|
||||
Keylet
|
||||
hookStateDir(AccountID const& id, uint256 const& ns) noexcept
|
||||
{
|
||||
@@ -159,6 +169,12 @@ emittedTxn(uint256 const& id) noexcept
|
||||
return {ltEMITTED_TXN, indexHash(LedgerNameSpace::EMITTED_TXN, id)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
exportedTxn(uint256 const& id) noexcept
|
||||
{
|
||||
return {ltEXPORTED_TXN, indexHash(LedgerNameSpace::EXPORTED_TXN, id)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
hook(AccountID const& id) noexcept
|
||||
{
|
||||
|
||||
@@ -103,6 +103,7 @@ CONSTRUCT_TYPED_SFIELD(sfHookEmitCount, "HookEmitCount", UINT16,
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookExecutionIndex, "HookExecutionIndex", UINT16, 19);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookApiVersion, "HookApiVersion", UINT16, 20);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookStateScale, "HookStateScale", UINT16, 21);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookExportCount, "HookExportCount", UINT16, 22);
|
||||
|
||||
// 32-bit integers (common)
|
||||
CONSTRUCT_TYPED_SFIELD(sfNetworkID, "NetworkID", UINT32, 1);
|
||||
|
||||
Reference in New Issue
Block a user