feat: pack entropy status metadata

This commit is contained in:
Nicholas Dudfield
2026-07-16 14:39:58 +07:00
parent bf6cf9c84a
commit ec5d22f557
9 changed files with 349 additions and 214 deletions

View File

@@ -361,9 +361,9 @@ xport_cancel(uint32_t read_ptr, uint32_t read_len, uint32_t flags);
1 = consensus_fallback, 2 = participant_aligned,
3 = validator_quorum, 4 = validator_full.
entropy_status writes five big-endian bytes:
tier:u8, count:u16, denominator:u16
and returns ledger age (0 current, 1 previous, >1 stale).
entropy_status returns a packed non-negative value:
bits 32..39 tier, 16..31 count, 0..15 denominator.
Check for a negative error before using the ENTROPY_* macros.
Classify tier before count/denominator arithmetic: fallback is tier 1
with count=denominator=0. Common policies are denominator-count <= 1,
@@ -380,7 +380,7 @@ extern int64_t
random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
extern int64_t
entropy_status(uint32_t write_ptr, uint32_t write_len);
entropy_status();
#ifdef __cplusplus
}

View File

@@ -50,9 +50,9 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
print " 1 = consensus_fallback, 2 = participant_aligned,";
print " 3 = validator_quorum, 4 = validator_full.";
print "";
print " entropy_status writes five big-endian bytes:";
print " tier:u8, count:u16, denominator:u16";
print " and returns ledger age (0 current, 1 previous, >1 stale).";
print " entropy_status returns a packed non-negative value:";
print " bits 32..39 tier, 16..31 count, 0..15 denominator.";
print " Check for a negative error before using the ENTROPY_* macros.";
print "";
print " Classify tier before count/denominator arithmetic: fallback is tier 1";
print " with count=denominator=0. Common policies are denominator-count <= 1,";

View File

@@ -145,6 +145,10 @@ int out_len = 0;\
#define SUB_OFFSET(x) ((int32_t)(x >> 32))
#define SUB_LENGTH(x) ((int32_t)(x & 0xFFFFFFFFULL))
#define ENTROPY_TIER(x) (((uint64_t)(x) >> 32U) & 0xFFU)
#define ENTROPY_COUNT(x) (((uint64_t)(x) >> 16U) & 0xFFFFU)
#define ENTROPY_DENOMINATOR(x) ((uint64_t)(x) & 0xFFFFU)
#define BUFFER_EQUAL_20(buf1, buf2)\
(\
*(((uint64_t*)(buf1)) + 0) == *(((uint64_t*)(buf2)) + 0) &&\
@@ -378,4 +382,3 @@ int out_len = 0;\
#endif

View File

@@ -398,7 +398,7 @@ HOOK_API_DEFINITION(
int64_t, random, (uint32_t, uint32_t, uint32_t),
featureConsensusEntropy)
// int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
// int64_t entropy_status();
HOOK_API_DEFINITION(
int64_t, entropy_status, (uint32_t, uint32_t),
int64_t, entropy_status, (),
featureConsensusEntropy)

View File

@@ -482,45 +482,34 @@ class ConsensusEntropy_test : public beast::unit_test::suite
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define OUT_OF_BOUNDS (-1)
#define TOO_SMALL (-4)
extern int64_t entropy_status();
#define ENTROPY_TIER(x) (((uint64_t)(x) >> 32U) & 0xFFU)
#define ENTROPY_COUNT(x) (((uint64_t)(x) >> 16U) & 0xFFFFU)
#define ENTROPY_DENOMINATOR(x) ((uint64_t)(x) & 0xFFFFU)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5] = {0xA5, 0xA5, 0xA5, 0xA5, 0xA5};
if (entropy_status((uint32_t)status, 4) != TOO_SMALL)
return accept(0, 0, 10);
for (int i = 0; GUARD(5), i < 5; ++i)
if (status[i] != 0xA5)
return accept(0, 0, 11);
if (entropy_status(0xFFFFFFFEU, 5) != OUT_OF_BOUNDS)
return accept(0, 0, 12);
int64_t age = entropy_status((uint32_t)status, 5);
if (age < 0 || age > 1)
int64_t status = entropy_status();
if (status < 0)
return accept(0, 0, 13);
uint32_t count = ((uint32_t)status[1] << 8) | status[2];
uint32_t denominator =
((uint32_t)status[3] << 8) | status[4];
if (status[0] != 3 || count != 19 || denominator != 20)
uint32_t tier = ENTROPY_TIER(status);
uint32_t count = ENTROPY_COUNT(status);
uint32_t denominator = ENTROPY_DENOMINATOR(status);
if (tier != 3 || count != 19 || denominator != 20)
return accept(0, 0, 14);
// Common caller-side policies: tolerate one absent, require
// 4/5 participation, and require an absolute floor of 19.
if (status[0] < 2 || denominator - count > 1)
if (tier < 2 || denominator - count > 1)
return accept(0, 0, 15);
if ((uint64_t)5 * count < (uint64_t)4 * denominator)
return accept(0, 0, 16);
if (count < 19)
return accept(0, 0, 17);
return accept(0, 0, age);
return accept(0, 0, 0);
}
)[test.hook]"];
@@ -541,8 +530,6 @@ class ConsensusEntropy_test : public beast::unit_test::suite
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
BEAST_REQUIRE(hookExecutions.size() == 1);
// Open-ledger preview accepts age 1; final ordered execution sees the
// current-ledger pseudo first and records age 0 in final metadata.
BEAST_EXPECT(hookReturnCode(hookExecutions[0]) == 0);
BEAST_EXPECT(hookExecutions[0].getFieldU8(sfHookResult) == 3);
}
@@ -581,18 +568,20 @@ class ConsensusEntropy_test : public beast::unit_test::suite
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
extern int64_t entropy_status();
#define ENTROPY_TIER(x) (((uint64_t)(x) >> 32U) & 0xFFU)
#define ENTROPY_COUNT(x) (((uint64_t)(x) >> 16U) & 0xFFFFU)
#define ENTROPY_DENOMINATOR(x) ((uint64_t)(x) & 0xFFFFU)
#define TOO_LITTLE_ENTROPY (-48)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5];
int64_t age = entropy_status((uint32_t)status, 5);
if (age < 0 || age > 1)
int64_t status = entropy_status();
if (status < 0)
return accept(0, 0, 20);
if (status[0] != 1 || status[1] != 0 || status[2] != 0 ||
status[3] != 0 || status[4] != 0)
if (ENTROPY_TIER(status) != 1 || ENTROPY_COUNT(status) != 0 ||
ENTROPY_DENOMINATOR(status) != 0)
return accept(0, 0, 21);
int64_t allowed = dice(6, 1);
@@ -601,7 +590,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
if (dice(6, 2) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 23);
return accept(0, 0, age);
return accept(0, 0, 0);
}
)[test.hook]"];
@@ -694,7 +683,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
void
testDiceWithoutAmendment()
{
testcase("Hook dice() import fails without ConsensusEntropy amendment");
testcase("Hook entropy imports independently require amendment");
using namespace jtx;
Env env{*this, supported_amendments()};
@@ -705,31 +694,126 @@ class ConsensusEntropy_test : public beast::unit_test::suite
BEAST_EXPECT(!env.le(keylet::consensusEntropy()));
// The CE hook APIs are amendment-scoped at install time. Without
// featureConsensusEntropy, a hook importing dice() is not installable,
// so the runtime path cannot be reached with the API disabled.
TestHook hook = consensusentropy_test_wasm[R"[test.hook](
TestHook diceHook = consensusentropy_test_wasm[R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5];
int64_t result = entropy_status((uint32_t)status, 5);
if (result >= 0)
result = dice(6, 3);
return accept(0, 0, result);
return accept(0, 0, dice(6, 3));
}
)[test.hook]"];
TestHook randomHook = consensusentropy_test_wasm[R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t buf[32];
return accept(0, 0, random((uint32_t)buf, 32, 3));
}
)[test.hook]"];
TestHook statusHook = consensusentropy_test_wasm[R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t entropy_status();
int64_t hook(uint32_t r)
{
_g(1,1);
return accept(0, 0, entropy_status());
}
)[test.hook]"];
env(ripple::test::jtx::hook(alice, {{hso(diceHook, overrideFlag)}}, 0),
M("set dice-no-amendment hook"),
HSFEE,
ter(temMALFORMED));
env(ripple::test::jtx::hook(
alice, {{hso(randomHook, overrideFlag)}}, 0),
M("set random-no-amendment hook"),
HSFEE,
ter(temMALFORMED));
env(ripple::test::jtx::hook(
alice, {{hso(statusHook, overrideFlag)}}, 0),
M("set entropy-status-no-amendment hook"),
HSFEE,
ter(temMALFORMED));
}
void
testRandomTierRequirementNotMet()
{
testcase("Hook random() fails before write below min_tier");
using namespace jtx;
Env env{
*this,
envconfig(),
supported_amendments() | featureConsensusEntropy,
nullptr};
ConsensusTestConfig cfg;
cfg.standaloneEntropyTier = entropyTierValidatorQuorum;
cfg.standaloneEntropyCount = 19;
cfg.standaloneEntropyDenominator = 20;
env.app().getRuntimeConfig().setGlobalConfig(cfg);
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env.close();
TestHook hook = consensusentropy_test_wasm[R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define TOO_LITTLE_ENTROPY (-48)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t buf[32];
for (int i = 0; GUARD(32), i < 32; ++i)
buf[i] = 0xA5;
if (random((uint32_t)buf, 32, 4) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 30);
for (int i = 0; GUARD(32), i < 32; ++i)
if (buf[i] != 0xA5)
return accept(0, 0, 31);
return accept(0, 0, 0);
}
)[test.hook]"];
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
M("set dice-no-amendment hook"),
HSFEE,
ter(temMALFORMED));
M("set random-tier-requirement hook"),
HSFEE);
env.close();
Json::Value invoke;
invoke[jss::TransactionType] = "Invoke";
invoke[jss::Account] = alice.human();
env(invoke, M("test random min_tier unmet"), fee(XRP(1)));
auto meta = env.meta();
BEAST_REQUIRE(meta);
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
BEAST_REQUIRE(hookExecutions.size() == 1);
BEAST_EXPECT(hookReturnCode(hookExecutions[0]) == 0);
BEAST_EXPECT(hookExecutions[0].getFieldU8(sfHookResult) == 3);
}
void
@@ -825,6 +909,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
testEntropyStatusFallback();
testDiceTierRequirementNotMet();
testDiceWithoutAmendment();
testRandomTierRequirementNotMet();
testInvalidEntropyRequirements();
testRandom();
testDiceConsecutiveCallsDiffer();

View File

@@ -221,95 +221,54 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define OUT_OF_BOUNDS (-1)
#define TOO_SMALL (-4)
extern int64_t entropy_status();
#define ENTROPY_TIER(x) (((uint64_t)(x) >> 32U) & 0xFFU)
#define ENTROPY_COUNT(x) (((uint64_t)(x) >> 16U) & 0xFFFFU)
#define ENTROPY_DENOMINATOR(x) ((uint64_t)(x) & 0xFFFFU)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5] = {0xA5, 0xA5, 0xA5, 0xA5, 0xA5};
if (entropy_status((uint32_t)status, 4) != TOO_SMALL)
return accept(0, 0, 10);
for (int i = 0; GUARD(5), i < 5; ++i)
if (status[i] != 0xA5)
return accept(0, 0, 11);
if (entropy_status(0xFFFFFFFEU, 5) != OUT_OF_BOUNDS)
return accept(0, 0, 12);
int64_t age = entropy_status((uint32_t)status, 5);
if (age < 0 || age > 1)
int64_t status = entropy_status();
if (status < 0)
return accept(0, 0, 13);
uint32_t count = ((uint32_t)status[1] << 8) | status[2];
uint32_t denominator =
((uint32_t)status[3] << 8) | status[4];
if (status[0] != 3 || count != 19 || denominator != 20)
uint32_t tier = ENTROPY_TIER(status);
uint32_t count = ENTROPY_COUNT(status);
uint32_t denominator = ENTROPY_DENOMINATOR(status);
if (tier != 3 || count != 19 || denominator != 20)
return accept(0, 0, 14);
// Common caller-side policies: tolerate one absent, require
// 4/5 participation, and require an absolute floor of 19.
if (status[0] < 2 || denominator - count > 1)
if (tier < 2 || denominator - count > 1)
return accept(0, 0, 15);
if ((uint64_t)5 * count < (uint64_t)4 * denominator)
return accept(0, 0, 16);
if (count < 19)
return accept(0, 0, 17);
return accept(0, 0, age);
return accept(0, 0, 0);
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x19U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU,
0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x2CU, 0x03U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0EU, 0x65U, 0x6EU,
0x74U, 0x72U, 0x6FU, 0x70U, 0x79U, 0x5FU, 0x73U, 0x74U, 0x61U,
0x74U, 0x75U, 0x73U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U,
0x03U, 0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U,
0x06U, 0x08U, 0x01U, 0x7FU, 0x01U, 0x41U, 0x80U, 0x80U, 0x04U,
0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU,
0x00U, 0x03U, 0x0AU, 0xB2U, 0x02U, 0x01U, 0xAFU, 0x02U, 0x01U,
0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x10U, 0x6BU, 0x22U, 0x00U,
0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U, 0x1AU,
0x20U, 0x00U, 0x41U, 0xA5U, 0x01U, 0x3AU, 0x00U, 0x0CU, 0x20U,
0x00U, 0x41U, 0xA5U, 0xCBU, 0x96U, 0xADU, 0x7AU, 0x36U, 0x02U,
0x08U, 0x02U, 0x40U, 0x20U, 0x00U, 0x41U, 0x08U, 0x6AU, 0x41U,
0x04U, 0x10U, 0x01U, 0x42U, 0x7CU, 0x52U, 0x04U, 0x40U, 0x42U,
0x0AU, 0x21U, 0x01U, 0x0CU, 0x01U, 0x0BU, 0x41U, 0x91U, 0x80U,
0x80U, 0x80U, 0x78U, 0x41U, 0x06U, 0x10U, 0x00U, 0x1AU, 0x42U,
0x0BU, 0x21U, 0x01U, 0x20U, 0x00U, 0x2DU, 0x00U, 0x08U, 0x41U,
0xA5U, 0x01U, 0x47U, 0x0DU, 0x00U, 0x41U, 0x91U, 0x80U, 0x80U,
0x80U, 0x78U, 0x41U, 0x06U, 0x10U, 0x00U, 0x1AU, 0x20U, 0x00U,
0x2DU, 0x00U, 0x09U, 0x41U, 0xA5U, 0x01U, 0x47U, 0x0DU, 0x00U,
0x41U, 0x91U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x06U, 0x10U,
0x00U, 0x1AU, 0x20U, 0x00U, 0x2DU, 0x00U, 0x0AU, 0x41U, 0xA5U,
0x01U, 0x47U, 0x0DU, 0x00U, 0x41U, 0x91U, 0x80U, 0x80U, 0x80U,
0x78U, 0x41U, 0x06U, 0x10U, 0x00U, 0x1AU, 0x20U, 0x00U, 0x2DU,
0x00U, 0x0BU, 0x41U, 0xA5U, 0x01U, 0x47U, 0x0DU, 0x00U, 0x41U,
0x91U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x06U, 0x10U, 0x00U,
0x1AU, 0x20U, 0x00U, 0x2DU, 0x00U, 0x0CU, 0x41U, 0xA5U, 0x01U,
0x47U, 0x0DU, 0x00U, 0x41U, 0x91U, 0x80U, 0x80U, 0x80U, 0x78U,
0x41U, 0x06U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x7EU, 0x41U, 0x05U,
0x10U, 0x01U, 0x42U, 0x7FU, 0x52U, 0x04U, 0x40U, 0x42U, 0x0CU,
0x21U, 0x01U, 0x0CU, 0x01U, 0x0BU, 0x20U, 0x00U, 0x41U, 0x08U,
0x6AU, 0x41U, 0x05U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x01U,
0x56U, 0x04U, 0x40U, 0x42U, 0x0DU, 0x21U, 0x01U, 0x0CU, 0x01U,
0x0BU, 0x42U, 0x0EU, 0x42U, 0x0EU, 0x42U, 0x0EU, 0x20U, 0x01U,
0x20U, 0x00U, 0x2DU, 0x00U, 0x0CU, 0x20U, 0x00U, 0x2DU, 0x00U,
0x0BU, 0x41U, 0x08U, 0x74U, 0x72U, 0x41U, 0x14U, 0x47U, 0x1BU,
0x20U, 0x00U, 0x2DU, 0x00U, 0x0AU, 0x20U, 0x00U, 0x2DU, 0x00U,
0x09U, 0x41U, 0x08U, 0x74U, 0x72U, 0x41U, 0x13U, 0x47U, 0x1BU,
0x20U, 0x00U, 0x2DU, 0x00U, 0x08U, 0x41U, 0x03U, 0x47U, 0x1BU,
0x21U, 0x01U, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U,
0x10U, 0x02U, 0x21U, 0x01U, 0x20U, 0x00U, 0x41U, 0x10U, 0x6AU,
0x24U, 0x00U, 0x20U, 0x01U, 0x0BU,
0x17U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x00U, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U,
0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U, 0x2CU, 0x03U,
0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U,
0x03U, 0x65U, 0x6EU, 0x76U, 0x0EU, 0x65U, 0x6EU, 0x74U, 0x72U,
0x6FU, 0x70U, 0x79U, 0x5FU, 0x73U, 0x74U, 0x61U, 0x74U, 0x75U,
0x73U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U,
0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U, 0x02U,
0x01U, 0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U,
0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x03U, 0x0AU,
0x33U, 0x01U, 0x31U, 0x01U, 0x01U, 0x7EU, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U,
0x0DU, 0x42U, 0x0EU, 0x42U, 0x00U, 0x10U, 0x01U, 0x22U, 0x01U,
0x42U, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0x1FU, 0x83U, 0x42U,
0x94U, 0x80U, 0xCCU, 0x80U, 0x30U, 0x52U, 0x1BU, 0x20U, 0x01U,
0x42U, 0x00U, 0x53U, 0x1BU, 0x10U, 0x02U, 0x0BU,
}},
/* ==== WASM: 5 ==== */
@@ -318,18 +277,20 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
extern int64_t entropy_status();
#define ENTROPY_TIER(x) (((uint64_t)(x) >> 32U) & 0xFFU)
#define ENTROPY_COUNT(x) (((uint64_t)(x) >> 16U) & 0xFFFFU)
#define ENTROPY_DENOMINATOR(x) ((uint64_t)(x) & 0xFFFFU)
#define TOO_LITTLE_ENTROPY (-48)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5];
int64_t age = entropy_status((uint32_t)status, 5);
if (age < 0 || age > 1)
int64_t status = entropy_status();
if (status < 0)
return accept(0, 0, 20);
if (status[0] != 1 || status[1] != 0 || status[2] != 0 ||
status[3] != 0 || status[4] != 0)
if (ENTROPY_TIER(status) != 1 || ENTROPY_COUNT(status) != 0 ||
ENTROPY_DENOMINATOR(status) != 0)
return accept(0, 0, 21);
int64_t allowed = dice(6, 1);
@@ -338,40 +299,33 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
if (dice(6, 2) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 23);
return accept(0, 0, age);
return accept(0, 0, 0);
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x19U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U,
0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU, 0x7FU,
0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x37U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0EU, 0x65U, 0x6EU,
0x74U, 0x72U, 0x6FU, 0x70U, 0x79U, 0x5FU, 0x73U, 0x74U, 0x61U,
0x74U, 0x75U, 0x73U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x04U, 0x64U, 0x69U, 0x63U, 0x65U, 0x00U, 0x00U, 0x03U, 0x65U,
0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U,
0x00U, 0x02U, 0x03U, 0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U,
0x00U, 0x01U, 0x06U, 0x08U, 0x01U, 0x7FU, 0x01U, 0x41U, 0x80U,
0x80U, 0x04U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU,
0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x90U, 0x01U, 0x01U, 0x8DU,
0x01U, 0x01U, 0x02U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x10U, 0x6BU,
0x22U, 0x00U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U,
0x00U, 0x1AU, 0x02U, 0x40U, 0x20U, 0x00U, 0x41U, 0x0BU, 0x6AU,
0x41U, 0x05U, 0x10U, 0x01U, 0x22U, 0x02U, 0x42U, 0x01U, 0x56U,
0x04U, 0x40U, 0x42U, 0x14U, 0x21U, 0x01U, 0x0CU, 0x01U, 0x0BU,
0x42U, 0x15U, 0x21U, 0x01U, 0x20U, 0x00U, 0x2DU, 0x00U, 0x0BU,
0x41U, 0x01U, 0x47U, 0x0DU, 0x00U, 0x20U, 0x00U, 0x2DU, 0x00U,
0x0CU, 0x0DU, 0x00U, 0x20U, 0x00U, 0x2DU, 0x00U, 0x0DU, 0x0DU,
0x00U, 0x20U, 0x00U, 0x2DU, 0x00U, 0x0EU, 0x0DU, 0x00U, 0x20U,
0x00U, 0x2DU, 0x00U, 0x0FU, 0x0DU, 0x00U, 0x41U, 0x06U, 0x41U,
0x01U, 0x10U, 0x02U, 0x42U, 0x05U, 0x56U, 0x04U, 0x40U, 0x42U,
0x16U, 0x21U, 0x01U, 0x0CU, 0x01U, 0x0BU, 0x42U, 0x17U, 0x20U,
0x02U, 0x41U, 0x06U, 0x41U, 0x02U, 0x10U, 0x02U, 0x42U, 0x50U,
0x52U, 0x1BU, 0x21U, 0x01U, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U,
0x20U, 0x01U, 0x10U, 0x03U, 0x21U, 0x01U, 0x20U, 0x00U, 0x41U,
0x10U, 0x6AU, 0x24U, 0x00U, 0x20U, 0x01U, 0x0BU,
0x1DU, 0x05U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x00U, 0x01U, 0x7EU, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU,
0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U,
0x7FU, 0x01U, 0x7EU, 0x02U, 0x37U, 0x04U, 0x03U, 0x65U, 0x6EU,
0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU,
0x76U, 0x0EU, 0x65U, 0x6EU, 0x74U, 0x72U, 0x6FU, 0x70U, 0x79U,
0x5FU, 0x73U, 0x74U, 0x61U, 0x74U, 0x75U, 0x73U, 0x00U, 0x01U,
0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U, 0x69U, 0x63U, 0x65U,
0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U,
0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x03U, 0x03U, 0x02U, 0x01U,
0x04U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U, 0x01U,
0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x54U,
0x01U, 0x52U, 0x01U, 0x01U, 0x7EU, 0x41U, 0x01U, 0x41U, 0x01U,
0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x02U, 0x7EU,
0x42U, 0x14U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x00U, 0x53U,
0x0DU, 0x00U, 0x1AU, 0x42U, 0x15U, 0x20U, 0x01U, 0x42U, 0xFFU,
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0x1FU, 0x83U, 0x42U, 0x80U, 0x80U,
0x80U, 0x80U, 0x10U, 0x52U, 0x0DU, 0x00U, 0x1AU, 0x42U, 0x16U,
0x41U, 0x06U, 0x41U, 0x01U, 0x10U, 0x02U, 0x42U, 0x05U, 0x56U,
0x0DU, 0x00U, 0x1AU, 0x42U, 0x17U, 0x42U, 0x00U, 0x41U, 0x06U,
0x41U, 0x02U, 0x10U, 0x02U, 0x42U, 0x50U, 0x52U, 0x1BU, 0x0BU,
0x10U, 0x03U, 0x0BU,
}},
/* ==== WASM: 6 ==== */
@@ -410,43 +364,156 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t status[5];
int64_t result = entropy_status((uint32_t)status, 5);
if (result >= 0)
result = dice(6, 3);
return accept(0, 0, result);
return accept(0, 0, dice(6, 3));
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x19U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U,
0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU, 0x7FU,
0x19U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU,
0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x37U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x0EU, 0x65U, 0x6EU,
0x74U, 0x72U, 0x6FU, 0x70U, 0x79U, 0x5FU, 0x73U, 0x74U, 0x61U,
0x74U, 0x75U, 0x73U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x04U, 0x64U, 0x69U, 0x63U, 0x65U, 0x00U, 0x00U, 0x03U, 0x65U,
0x22U, 0x03U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U, 0x69U,
0x63U, 0x65U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U,
0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U,
0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U,
0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x03U,
0x0AU, 0x17U, 0x01U, 0x15U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U,
0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x41U, 0x06U,
0x41U, 0x03U, 0x10U, 0x01U, 0x10U, 0x02U, 0x0BU,
}},
/* ==== WASM: 8 ==== */
{R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t buf[32];
return accept(0, 0, random((uint32_t)buf, 32, 3));
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x1AU, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x03U, 0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU,
0x02U, 0x24U, 0x03U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU,
0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x72U,
0x61U, 0x6EU, 0x64U, 0x6FU, 0x6DU, 0x00U, 0x01U, 0x03U, 0x65U,
0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U,
0x00U, 0x02U, 0x03U, 0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U,
0x00U, 0x01U, 0x06U, 0x08U, 0x01U, 0x7FU, 0x01U, 0x41U, 0x80U,
0x80U, 0x04U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU,
0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x41U, 0x01U, 0x3FU, 0x01U,
0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x10U, 0x6BU, 0x22U, 0x00U,
0x6FU, 0x6BU, 0x00U, 0x03U, 0x0AU, 0x2FU, 0x01U, 0x2DU, 0x01U,
0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x20U, 0x6BU, 0x22U, 0x00U,
0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U, 0x1AU,
0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x00U, 0x41U, 0x0BU, 0x6AU,
0x41U, 0x05U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x00U, 0x59U,
0x04U, 0x7EU, 0x41U, 0x06U, 0x41U, 0x03U, 0x10U, 0x02U, 0x05U,
0x20U, 0x01U, 0x0BU, 0x10U, 0x03U, 0x21U, 0x01U, 0x20U, 0x00U,
0x41U, 0x10U, 0x6AU, 0x24U, 0x00U, 0x20U, 0x01U, 0x0BU,
0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x00U, 0x41U, 0x20U, 0x41U,
0x03U, 0x10U, 0x01U, 0x10U, 0x02U, 0x21U, 0x01U, 0x20U, 0x00U,
0x41U, 0x20U, 0x6AU, 0x24U, 0x00U, 0x20U, 0x01U, 0x0BU,
}},
/* ==== WASM: 8 ==== */
/* ==== WASM: 9 ==== */
{R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t entropy_status();
int64_t hook(uint32_t r)
{
_g(1,1);
return accept(0, 0, entropy_status());
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x17U, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x00U, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U,
0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U, 0x2CU, 0x03U,
0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U, 0x00U, 0x00U,
0x03U, 0x65U, 0x6EU, 0x76U, 0x0EU, 0x65U, 0x6EU, 0x74U, 0x72U,
0x6FU, 0x70U, 0x79U, 0x5FU, 0x73U, 0x74U, 0x61U, 0x74U, 0x75U,
0x73U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U,
0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U, 0x02U,
0x01U, 0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U,
0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x03U, 0x0AU,
0x13U, 0x01U, 0x11U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U,
0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x10U, 0x01U, 0x10U,
0x02U, 0x0BU,
}},
/* ==== WASM: 10 ==== */
{R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);
extern int64_t accept(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define TOO_LITTLE_ENTROPY (-48)
int64_t hook(uint32_t r)
{
_g(1,1);
uint8_t buf[32];
for (int i = 0; GUARD(32), i < 32; ++i)
buf[i] = 0xA5;
if (random((uint32_t)buf, 32, 4) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 30);
for (int i = 0; GUARD(32), i < 32; ++i)
if (buf[i] != 0xA5)
return accept(0, 0, 31);
return accept(0, 0, 0);
}
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x1AU, 0x04U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x03U, 0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU,
0x02U, 0x24U, 0x03U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU,
0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x72U,
0x61U, 0x6EU, 0x64U, 0x6FU, 0x6DU, 0x00U, 0x01U, 0x03U, 0x65U,
0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U,
0x00U, 0x02U, 0x03U, 0x02U, 0x01U, 0x03U, 0x05U, 0x03U, 0x01U,
0x00U, 0x01U, 0x06U, 0x08U, 0x01U, 0x7FU, 0x01U, 0x41U, 0x80U,
0x80U, 0x04U, 0x0BU, 0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU,
0x6FU, 0x6BU, 0x00U, 0x03U, 0x0AU, 0xC7U, 0x01U, 0x01U, 0xC4U,
0x01U, 0x02U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x23U, 0x00U, 0x41U,
0x20U, 0x6BU, 0x22U, 0x01U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x8DU, 0x80U, 0x80U, 0x80U,
0x78U, 0x41U, 0x21U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x21U,
0x00U, 0x03U, 0x40U, 0x41U, 0x8DU, 0x80U, 0x80U, 0x80U, 0x78U,
0x41U, 0x21U, 0x10U, 0x00U, 0x1AU, 0x20U, 0x00U, 0x20U, 0x01U,
0x6AU, 0x41U, 0xA5U, 0x01U, 0x3AU, 0x00U, 0x00U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x20U, 0x00U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x00U, 0x41U, 0x20U,
0x47U, 0x0DU, 0x00U, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x02U,
0x7EU, 0x42U, 0x1EU, 0x20U, 0x01U, 0x41U, 0x20U, 0x41U, 0x04U,
0x10U, 0x01U, 0x42U, 0x50U, 0x52U, 0x0DU, 0x00U, 0x1AU, 0x41U,
0x92U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U, 0x00U,
0x1AU, 0x41U, 0x00U, 0x21U, 0x00U, 0x02U, 0x40U, 0x03U, 0x40U,
0x41U, 0x92U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U,
0x00U, 0x1AU, 0x20U, 0x00U, 0x20U, 0x01U, 0x6AU, 0x2DU, 0x00U,
0x00U, 0x41U, 0xA5U, 0x01U, 0x47U, 0x0DU, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x20U, 0x00U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x00U, 0x41U, 0x20U,
0x47U, 0x0DU, 0x00U, 0x0BU, 0x42U, 0x00U, 0x0CU, 0x01U, 0x0BU,
0x42U, 0x1FU, 0x0BU, 0x10U, 0x02U, 0x21U, 0x02U, 0x20U, 0x01U,
0x41U, 0x20U, 0x6AU, 0x24U, 0x00U, 0x20U, 0x02U, 0x0BU,
}},
/* ==== WASM: 11 ==== */
{R"[test.hook](
#include <stdint.h>
extern int32_t _g(uint32_t, uint32_t);

View File

@@ -131,8 +131,8 @@ Hooks state `min_tier` explicitly on every draw (no hidden network default).
Entropy is served iff it is **fresh** (current or previous ledger) **and** meets
that class floor; otherwise the call **fails closed**
(`TOO_LITTLE_ENTROPY`). `entropy_status()` separately exposes the stored tier,
contributor count, denominator, and ledger age so hooks can impose proportional
or absolute policies without freezing those policies into the host ABI.
contributor count, and denominator so hooks can impose proportional or absolute
policies without freezing those policies into the host ABI.
Fallback is tier 1 with count/denominator `0/0`, so callers must classify tier
before arithmetic. Draws are also domain-separated by the hook execution role
that can share a transaction and hook hash: strong vs weak, callback vs direct

View File

@@ -383,9 +383,10 @@ fallback-grade randomness must do so explicitly at the call site. Valid
`INVALID_ARGUMENT`, while valid-but-unmet requirements return
`TOO_LITTLE_ENTROPY`.
`entropy_status(write_ptr, write_len)` writes the fixed five-byte big-endian
tuple `(tier:u8, count:u16, denominator:u16)` and returns the metadata's ledger
age. This lets Hook code implement policies such as one-absent tolerance,
`entropy_status()` returns a packed non-negative scalar with tier in bits
32..39, contributor count in bits 16..31, and denominator in bits 0..15.
Negative values remain Hook API errors. This lets Hook code implement policies
such as one-absent tolerance,
proportional participation, or an absolute floor without widening the frozen
draw API. It exposes no digest. Callers must classify tier before count or
denominator arithmetic because fallback deliberately reports tier 1 and
@@ -393,9 +394,9 @@ denominator arithmetic because fallback deliberately reports tier 1 and
Open-ledger hook execution is provisional. During speculative open-ledger
execution, `dice()`/`random()` and `entropy_status()` can only use the previous
ledger's finalized entropy (status age 1); final buildLCL execution sees the
current ledger's entropy pseudo-tx after it updates the SLE (status age 0).
Hooks that need final entropy must treat open-ledger RNG results as previews.
ledger's finalized entropy; final buildLCL execution sees the current ledger's
entropy pseudo-tx after it updates the SLE. Hooks that need final entropy must
treat open-ledger RNG results as previews.
The fallback digest derives from the agreed pre-injection tx set hash to avoid
circularity, and entropy pseudo-tx deduplication is value-based: if the agreed

View File

@@ -4266,38 +4266,17 @@ DEFINE_HOOK_FUNCTION(
HOOK_TEARDOWN();
}
DEFINE_HOOK_FUNCTION(
int64_t,
entropy_status,
uint32_t write_ptr,
uint32_t write_len)
DEFINE_HOOK_FUNCTION(int64_t, entropy_status)
{
HOOK_SETUP();
constexpr std::size_t statusSize = 5;
if (write_len < statusSize)
return TOO_SMALL;
if (NOT_IN_BOUNDS(write_ptr, statusSize, memory_length))
return OUT_OF_BOUNDS;
auto snapshot = readEntropySnapshot(view);
if (std::holds_alternative<hook_api::hook_return_code>(snapshot))
return std::get<hook_api::hook_return_code>(snapshot);
auto const& entropy = std::get<EntropySnapshot>(snapshot);
std::array<std::uint8_t, statusSize> const status{
entropy.tier,
static_cast<std::uint8_t>(entropy.count >> 8),
static_cast<std::uint8_t>(entropy.count),
static_cast<std::uint8_t>(entropy.denominator >> 8),
static_cast<std::uint8_t>(entropy.denominator)};
if (!WasmEdge_ResultOK(WasmEdge_MemoryInstanceSetData(
memoryCtx, status.data(), write_ptr, status.size())))
return INTERNAL_ERROR;
return entropy.age;
return (std::uint64_t{entropy.tier} << 32U) |
(std::uint64_t{entropy.count} << 16U) | entropy.denominator;
HOOK_TEARDOWN();
}