feat: simplify entropy Hook quality gates

This commit is contained in:
Nicholas Dudfield
2026-07-16 14:11:51 +07:00
parent 142fde5acd
commit bf6cf9c84a
8 changed files with 644 additions and 313 deletions

View File

@@ -357,24 +357,30 @@ xport_cancel(uint32_t read_ptr, uint32_t read_len, uint32_t flags);
/*
Consensus entropy APIs.
min_tier is a fail-closed floor:
1 = consensus_fallback, 2 = participant_aligned, 3 = validator_quorum.
min_count is the minimum validator/reveal count the caller accepts.
min_tier is a required fail-closed floor:
1 = consensus_fallback, 2 = participant_aligned,
3 = validator_quorum, 4 = validator_full.
If the most recent finalized entropy object does not satisfy both floors,
these APIs return TOO_LITTLE_ENTROPY. Open-ledger and simulate execution
are provisional previews over the entropy currently visible to the node;
final ordered ledger execution may see a different entropy object.
entropy_status writes five big-endian bytes:
tier:u8, count:u16, denominator:u16
and returns ledger age (0 current, 1 previous, >1 stale).
Classify tier before count/denominator arithmetic: fallback is tier 1
with count=denominator=0. Common policies are denominator-count <= 1,
5*count >= 4*denominator (use widened arithmetic), or count >= floor.
dice/random return TOO_LITTLE_ENTROPY if fresh visible entropy is below
min_tier. Open-ledger and simulate execution are provisional previews;
final ordered execution may see a different entropy object.
*/
extern int64_t
dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
dice(uint32_t sides, uint32_t min_tier);
extern int64_t
random(
uint32_t write_ptr,
uint32_t write_len,
uint32_t min_tier,
uint32_t min_count);
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);
#ifdef __cplusplus
}

View File

@@ -46,14 +46,21 @@ APPLY_HOOK="$SCRIPT_DIR/../include/xrpl/hook/hook_api.macro"
print "/*";
print " Consensus entropy APIs.";
print "";
print " min_tier is a fail-closed floor:";
print " 1 = consensus_fallback, 2 = participant_aligned, 3 = validator_quorum.";
print " min_count is the minimum validator/reveal count the caller accepts.";
print " min_tier is a required fail-closed floor:";
print " 1 = consensus_fallback, 2 = participant_aligned,";
print " 3 = validator_quorum, 4 = validator_full.";
print "";
print " If the most recent finalized entropy object does not satisfy both floors,";
print " these APIs return TOO_LITTLE_ENTROPY. Open-ledger and simulate execution";
print " are provisional previews over the entropy currently visible to the node;";
print " final ordered ledger execution may see a different entropy object.";
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 "";
print " Classify tier before count/denominator arithmetic: fallback is tier 1";
print " with count=denominator=0. Common policies are denominator-count <= 1,";
print " 5*count >= 4*denominator (use widened arithmetic), or count >= floor.";
print "";
print " dice/random return TOO_LITTLE_ENTROPY if fresh visible entropy is below";
print " min_tier. Open-ledger and simulate execution are provisional previews;";
print " final ordered execution may see a different entropy object.";
print "*/";
}

View File

@@ -388,12 +388,17 @@ HOOK_API_DEFINITION(
int64_t, xport_cancel, (uint32_t, uint32_t, uint32_t),
featureExport)
// int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
// int64_t dice(uint32_t sides, uint32_t min_tier);
HOOK_API_DEFINITION(
int64_t, dice, (uint32_t, uint32_t, uint32_t),
int64_t, dice, (uint32_t, uint32_t),
featureConsensusEntropy)
// int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier, uint32_t min_count);
// int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
HOOK_API_DEFINITION(
int64_t, random, (uint32_t, uint32_t, uint32_t, uint32_t),
int64_t, random, (uint32_t, uint32_t, uint32_t),
featureConsensusEntropy)
// int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
HOOK_API_DEFINITION(
int64_t, entropy_status, (uint32_t, uint32_t),
featureConsensusEntropy)

View File

@@ -181,7 +181,7 @@ 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 rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
int64_t hook(uint32_t r)
@@ -189,7 +189,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
_g(1,1);
// dice(6) should return 0..5
int64_t result = dice(6, 3, 5);
int64_t result = dice(6, 3);
// negative means error
if (result < 0)
@@ -256,7 +256,7 @@ 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 rollback(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, uint32_t min_count);
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)
int64_t hook(uint32_t r)
@@ -267,7 +267,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
for (int i = 0; GUARD(32), i < 32; ++i)
buf[i] = 0;
int64_t result = random((uint32_t)buf, 32, 3, 5);
int64_t result = random((uint32_t)buf, 32, 3);
// Should return 32 (bytes written)
if (result != 32)
@@ -332,16 +332,16 @@ 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 rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t r1 = dice(1000000, 3, 5);
int64_t r1 = dice(1000000, 3);
if (r1 < 0)
rollback(0, 0, r1);
int64_t r2 = dice(1000000, 3, 5);
int64_t r2 = dice(1000000, 3);
if (r2 < 0)
rollback(0, 0, r2);
@@ -409,12 +409,12 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(0, 3, 5);
int64_t result = dice(0, 3);
// dice(0) should return negative error code, pass it through
return accept(0, 0, result);
}
@@ -450,9 +450,9 @@ class ConsensusEntropy_test : public beast::unit_test::suite
}
void
testDiceRequirementNotMet()
testEntropyStatus()
{
testcase("Hook dice() returns TOO_LITTLE_ENTROPY below requirement");
testcase("Hook entropy_status() metadata and policy recipes");
using namespace jtx;
Env env{
@@ -461,41 +461,78 @@ class ConsensusEntropy_test : public beast::unit_test::suite
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();
BEAST_REQUIRE(env.le(keylet::consensusEntropy()));
auto const sle = env.le(keylet::consensusEntropy());
BEAST_REQUIRE(sle);
BEAST_EXPECT(
sle->getFieldU8(sfEntropyTier) == entropyTierValidatorQuorum);
BEAST_EXPECT(sle->getFieldU16(sfEntropyCount) == 19);
BEAST_EXPECT(sle->getFieldU16(sfEntropyDenominator) == 20);
// Standalone entropy carries EntropyCount=20,
// EntropyDenominator=20, and tier validator_full.
// A hook demanding min_count=21 states a requirement this ledger
// cannot meet, so dice must fail closed with TOO_LITTLE_ENTROPY (-48)
// rather than silently serving weaker entropy.
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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
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)
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(6, 3, 21);
// requirement unmet: pass the error code through
return accept(0, 0, result);
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)
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)
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)
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);
}
)[test.hook]"];
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
M("set dice-requirement hook"),
M("set entropy-status hook"),
HSFEE);
env.close();
Json::Value invoke;
invoke[jss::TransactionType] = "Invoke";
invoke[jss::Account] = alice.human();
env(invoke, M("test dice min_count unmet"), fee(XRP(1)));
env(invoke, M("test entropy status"), fee(XRP(1)));
auto meta = env.meta();
BEAST_REQUIRE(meta);
@@ -504,13 +541,86 @@ class ConsensusEntropy_test : public beast::unit_test::suite
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
BEAST_REQUIRE(hookExecutions.size() == 1);
auto const rawCode = hookExecutions[0].getFieldU64(sfHookReturnCode);
int64_t returnCode = (rawCode & 0x8000000000000000ULL)
? -static_cast<int64_t>(rawCode & 0x7FFFFFFFFFFFFFFFULL)
: static_cast<int64_t>(rawCode);
std::cerr << " dice(6,3,21) returnCode = " << returnCode << " (raw 0x"
<< std::hex << rawCode << std::dec << ")\n";
BEAST_EXPECT(returnCode == -48); // TOO_LITTLE_ENTROPY
// 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);
}
void
testEntropyStatusFallback()
{
testcase("Hook entropy_status() classifies fallback before arithmetic");
using namespace jtx;
Env env{
*this,
envconfig(),
supported_amendments() | featureConsensusEntropy,
nullptr};
ConsensusTestConfig cfg;
cfg.standaloneEntropyTier = entropyTierConsensusFallback;
cfg.standaloneEntropyCount = 0;
cfg.standaloneEntropyDenominator = 0;
env.app().getRuntimeConfig().setGlobalConfig(cfg);
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env.close();
auto const sle = env.le(keylet::consensusEntropy());
BEAST_REQUIRE(sle);
BEAST_EXPECT(
sle->getFieldU8(sfEntropyTier) == entropyTierConsensusFallback);
BEAST_EXPECT(sle->getFieldU16(sfEntropyCount) == 0);
BEAST_EXPECT(sle->getFieldU16(sfEntropyDenominator) == 0);
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 dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
#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)
return accept(0, 0, 20);
if (status[0] != 1 || status[1] != 0 || status[2] != 0 ||
status[3] != 0 || status[4] != 0)
return accept(0, 0, 21);
int64_t allowed = dice(6, 1);
if (allowed < 0 || allowed > 5)
return accept(0, 0, 22);
if (dice(6, 2) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 23);
return accept(0, 0, age);
}
)[test.hook]"];
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
M("set fallback entropy-status hook"),
HSFEE);
env.close();
Json::Value invoke;
invoke[jss::TransactionType] = "Invoke";
invoke[jss::Account] = alice.human();
env(invoke, M("test fallback entropy status"), 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);
}
@@ -550,12 +660,12 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(6, 4, 0);
int64_t result = dice(6, 4);
return accept(0, 0, result);
}
)[test.hook]"];
@@ -602,12 +712,16 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
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);
int64_t result = dice(6, 3, 0);
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);
}
)[test.hook]"];
@@ -640,8 +754,8 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
#define INVALID_ARGUMENT (-7)
int64_t hook(uint32_t r)
@@ -649,25 +763,25 @@ class ConsensusEntropy_test : public beast::unit_test::suite
_g(1,1);
uint8_t buf[32];
int64_t bad_min_tier = dice(6, 0, 0);
int64_t bad_min_tier = dice(6, 0);
if (bad_min_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_min_tier);
int64_t ok_full_tier = dice(6, 4, 0);
int64_t ok_full_tier = dice(6, 4);
if (ok_full_tier < 0 || ok_full_tier > 5)
return accept(0, 0, ok_full_tier);
int64_t bad_high_tier = dice(6, 5, 0);
int64_t bad_high_tier = dice(6, 5);
if (bad_high_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_high_tier);
int64_t bad_min_count = dice(6, 3, 70000);
if (bad_min_count != INVALID_ARGUMENT)
return accept(0, 0, bad_min_count);
int64_t bad_random_low = random((uint32_t)buf, 32, 0);
if (bad_random_low != INVALID_ARGUMENT)
return accept(0, 0, bad_random_low);
int64_t bad_random_tier = random((uint32_t)buf, 32, 5, 0);
if (bad_random_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_random_tier);
int64_t bad_random_high = random((uint32_t)buf, 32, 5);
if (bad_random_high != INVALID_ARGUMENT)
return accept(0, 0, bad_random_high);
// Sentinel distinct from any dice (0..5) / random result and
// from INVALID_ARGUMENT, so a regression that lets a bad
@@ -693,7 +807,7 @@ class ConsensusEntropy_test : public beast::unit_test::suite
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
BEAST_REQUIRE(hookExecutions.size() == 1);
// 42 only if all four invalid requirements were rejected; any bad
// 42 only if all invalid requirements were rejected; any bad
// requirement leaking through returns its own (non-42) code.
BEAST_EXPECT(hookReturnCode(hookExecutions[0]) == 42);
BEAST_EXPECT(hookExecutions[0].getFieldU8(sfHookResult) == 3);
@@ -707,7 +821,8 @@ class ConsensusEntropy_test : public beast::unit_test::suite
testNoSLEWithoutAmendment();
testDice();
testDiceZeroSides();
testDiceRequirementNotMet();
testEntropyStatus();
testEntropyStatusFallback();
testDiceTierRequirementNotMet();
testDiceWithoutAmendment();
testInvalidEntropyRequirements();

View File

@@ -16,7 +16,7 @@ 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 rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
int64_t hook(uint32_t r)
@@ -24,7 +24,7 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
_g(1,1);
// dice(6) should return 0..5
int64_t result = dice(6, 3, 5);
int64_t result = dice(6, 3);
// negative means error
if (result < 0)
@@ -39,25 +39,24 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x1AU, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU,
0x02U, 0x31U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU,
0x67U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U,
0x69U, 0x63U, 0x65U, 0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x08U, 0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U, 0x6BU,
0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U,
0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x00U, 0x03U, 0x02U, 0x01U,
0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U, 0x01U,
0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x40U,
0x01U, 0x3EU, 0x01U, 0x02U, 0x7EU, 0x41U, 0x01U, 0x41U, 0x01U,
0x10U, 0x00U, 0x1AU, 0x41U, 0x06U, 0x41U, 0x03U, 0x41U, 0x05U,
0x10U, 0x01U, 0x22U, 0x01U, 0x21U, 0x02U, 0x02U, 0x40U, 0x20U,
0x01U, 0x42U, 0x00U, 0x59U, 0x04U, 0x40U, 0x42U, 0x7FU, 0x21U,
0x02U, 0x20U, 0x01U, 0x42U, 0x06U, 0x54U, 0x0DU, 0x01U, 0x0BU,
0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x02U, 0x10U, 0x02U, 0x1AU,
0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x03U,
0x0BU,
0x19U, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x02U, 0x7FU,
0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x31U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U, 0x69U,
0x63U, 0x65U, 0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x08U,
0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U, 0x6BU, 0x00U,
0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U,
0x65U, 0x70U, 0x74U, 0x00U, 0x00U, 0x03U, 0x02U, 0x01U, 0x03U,
0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U, 0x01U, 0x04U,
0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x3EU, 0x01U,
0x3CU, 0x01U, 0x02U, 0x7EU, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U,
0x00U, 0x1AU, 0x41U, 0x06U, 0x41U, 0x03U, 0x10U, 0x01U, 0x22U,
0x01U, 0x21U, 0x02U, 0x02U, 0x40U, 0x20U, 0x01U, 0x42U, 0x00U,
0x59U, 0x04U, 0x40U, 0x42U, 0x7FU, 0x21U, 0x02U, 0x20U, 0x01U,
0x42U, 0x06U, 0x54U, 0x0DU, 0x01U, 0x0BU, 0x41U, 0x00U, 0x41U,
0x00U, 0x20U, 0x02U, 0x10U, 0x02U, 0x1AU, 0x0BU, 0x41U, 0x00U,
0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x03U, 0x0BU,
}},
/* ==== WASM: 1 ==== */
@@ -66,7 +65,7 @@ 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 rollback(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, uint32_t min_count);
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)
int64_t hook(uint32_t r)
@@ -77,7 +76,7 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
for (int i = 0; GUARD(32), i < 32; ++i)
buf[i] = 0;
int64_t result = random((uint32_t)buf, 32, 3, 5);
int64_t result = random((uint32_t)buf, 32, 3);
// Should return 32 (bytes written)
if (result != 32)
@@ -96,42 +95,42 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x1BU, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x04U, 0x7FU,
0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U,
0x7EU, 0x02U, 0x33U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U,
0x5FU, 0x67U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U,
0x72U, 0x61U, 0x6EU, 0x64U, 0x6FU, 0x6DU, 0x00U, 0x02U, 0x03U,
0x65U, 0x6EU, 0x76U, 0x08U, 0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U,
0x61U, 0x63U, 0x6BU, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x06U, 0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x00U,
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, 0xD0U, 0x01U, 0x01U, 0xCDU, 0x01U, 0x02U,
0x03U, 0x7FU, 0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x20U, 0x6BU,
0x22U, 0x01U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U,
0x00U, 0x1AU, 0x41U, 0x8EU, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U,
0x21U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x21U, 0x00U, 0x03U,
0x40U, 0x41U, 0x8EU, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U,
0x10U, 0x00U, 0x1AU, 0x20U, 0x00U, 0x20U, 0x01U, 0x6AU, 0x41U,
0x00U, 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, 0x20U, 0x01U, 0x41U, 0x20U, 0x41U, 0x03U, 0x41U, 0x05U,
0x10U, 0x01U, 0x22U, 0x04U, 0x42U, 0x20U, 0x52U, 0x04U, 0x40U,
0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x04U, 0x10U, 0x02U, 0x1AU,
0x0BU, 0x41U, 0x99U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U,
0x1AU, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU,
0x02U, 0x33U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU,
0x67U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x72U,
0x61U, 0x6EU, 0x64U, 0x6FU, 0x6DU, 0x00U, 0x02U, 0x03U, 0x65U,
0x6EU, 0x76U, 0x08U, 0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U,
0x63U, 0x6BU, 0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U,
0x61U, 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x00U, 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, 0xCEU, 0x01U, 0x01U, 0xCBU, 0x01U, 0x02U, 0x03U,
0x7FU, 0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x20U, 0x6BU, 0x22U,
0x01U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U,
0x1AU, 0x41U, 0x8EU, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U,
0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x21U, 0x00U, 0x03U, 0x40U,
0x41U, 0x99U, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U,
0x00U, 0x1AU, 0x20U, 0x00U, 0x20U, 0x01U, 0x6AU, 0x2DU, 0x00U,
0x00U, 0x21U, 0x03U, 0x41U, 0x01U, 0x20U, 0x02U, 0x20U, 0x03U,
0x1BU, 0x21U, 0x02U, 0x20U, 0x00U, 0x41U, 0x01U, 0x6AU, 0x22U,
0x00U, 0x41U, 0x20U, 0x47U, 0x0DU, 0x00U, 0x0BU, 0x20U, 0x02U,
0x45U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x7EU,
0x10U, 0x02U, 0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U,
0x00U, 0x10U, 0x03U, 0x21U, 0x04U, 0x20U, 0x01U, 0x41U, 0x20U,
0x6AU, 0x24U, 0x00U, 0x20U, 0x04U, 0x0BU,
0x41U, 0x8EU, 0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U,
0x00U, 0x1AU, 0x20U, 0x00U, 0x20U, 0x01U, 0x6AU, 0x41U, 0x00U,
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,
0x20U, 0x01U, 0x41U, 0x20U, 0x41U, 0x03U, 0x10U, 0x01U, 0x22U,
0x04U, 0x42U, 0x20U, 0x52U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U,
0x00U, 0x20U, 0x04U, 0x10U, 0x02U, 0x1AU, 0x0BU, 0x41U, 0x99U,
0x80U, 0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U, 0x00U, 0x1AU,
0x41U, 0x00U, 0x21U, 0x00U, 0x03U, 0x40U, 0x41U, 0x99U, 0x80U,
0x80U, 0x80U, 0x78U, 0x41U, 0x21U, 0x10U, 0x00U, 0x1AU, 0x20U,
0x00U, 0x20U, 0x01U, 0x6AU, 0x2DU, 0x00U, 0x00U, 0x21U, 0x03U,
0x41U, 0x01U, 0x20U, 0x02U, 0x20U, 0x03U, 0x1BU, 0x21U, 0x02U,
0x20U, 0x00U, 0x41U, 0x01U, 0x6AU, 0x22U, 0x00U, 0x41U, 0x20U,
0x47U, 0x0DU, 0x00U, 0x0BU, 0x20U, 0x02U, 0x45U, 0x04U, 0x40U,
0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x7EU, 0x10U, 0x02U, 0x1AU,
0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x00U, 0x10U, 0x03U,
0x21U, 0x04U, 0x20U, 0x01U, 0x41U, 0x20U, 0x6AU, 0x24U, 0x00U,
0x20U, 0x04U, 0x0BU,
}},
/* ==== WASM: 2 ==== */
@@ -140,16 +139,16 @@ 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 rollback(uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t r1 = dice(1000000, 3, 5);
int64_t r1 = dice(1000000, 3);
if (r1 < 0)
rollback(0, 0, r1);
int64_t r2 = dice(1000000, 3, 5);
int64_t r2 = dice(1000000, 3);
if (r2 < 0)
rollback(0, 0, r2);
@@ -162,28 +161,28 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x1AU, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU,
0x02U, 0x31U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU,
0x67U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U,
0x69U, 0x63U, 0x65U, 0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U,
0x08U, 0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U, 0x6BU,
0x00U, 0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U,
0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x00U, 0x03U, 0x02U, 0x01U,
0x03U, 0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U, 0x01U,
0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x62U,
0x01U, 0x60U, 0x01U, 0x02U, 0x7EU, 0x41U, 0x01U, 0x41U, 0x01U,
0x10U, 0x00U, 0x1AU, 0x41U, 0xC0U, 0x84U, 0x3DU, 0x41U, 0x03U,
0x41U, 0x05U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x00U, 0x53U,
0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U,
0x02U, 0x1AU, 0x0BU, 0x41U, 0xC0U, 0x84U, 0x3DU, 0x41U, 0x03U,
0x41U, 0x05U, 0x10U, 0x01U, 0x22U, 0x02U, 0x42U, 0x00U, 0x53U,
0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x02U, 0x10U,
0x02U, 0x1AU, 0x0BU, 0x20U, 0x01U, 0x20U, 0x02U, 0x51U, 0x04U,
0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x7FU, 0x10U, 0x02U,
0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x02U, 0x42U,
0x14U, 0x86U, 0x20U, 0x01U, 0x84U, 0x10U, 0x03U, 0x0BU,
0x19U, 0x04U, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7EU, 0x01U, 0x7EU,
0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U, 0x02U, 0x7FU,
0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x31U, 0x04U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x02U, 0x5FU, 0x67U,
0x00U, 0x01U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x04U, 0x64U, 0x69U,
0x63U, 0x65U, 0x00U, 0x02U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x08U,
0x72U, 0x6FU, 0x6CU, 0x6CU, 0x62U, 0x61U, 0x63U, 0x6BU, 0x00U,
0x00U, 0x03U, 0x65U, 0x6EU, 0x76U, 0x06U, 0x61U, 0x63U, 0x63U,
0x65U, 0x70U, 0x74U, 0x00U, 0x00U, 0x03U, 0x02U, 0x01U, 0x03U,
0x05U, 0x03U, 0x01U, 0x00U, 0x01U, 0x07U, 0x08U, 0x01U, 0x04U,
0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U, 0x04U, 0x0AU, 0x5EU, 0x01U,
0x5CU, 0x01U, 0x02U, 0x7EU, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U,
0x00U, 0x1AU, 0x41U, 0xC0U, 0x84U, 0x3DU, 0x41U, 0x03U, 0x10U,
0x01U, 0x22U, 0x01U, 0x42U, 0x00U, 0x53U, 0x04U, 0x40U, 0x41U,
0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U, 0x1AU, 0x0BU,
0x41U, 0xC0U, 0x84U, 0x3DU, 0x41U, 0x03U, 0x10U, 0x01U, 0x22U,
0x02U, 0x42U, 0x00U, 0x53U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U,
0x00U, 0x20U, 0x02U, 0x10U, 0x02U, 0x1AU, 0x0BU, 0x20U, 0x01U,
0x20U, 0x02U, 0x51U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U,
0x42U, 0x7FU, 0x10U, 0x02U, 0x1AU, 0x0BU, 0x41U, 0x00U, 0x41U,
0x00U, 0x20U, 0x02U, 0x42U, 0x14U, 0x86U, 0x20U, 0x01U, 0x84U,
0x10U, 0x03U, 0x0BU,
}},
/* ==== WASM: 3 ==== */
@@ -191,31 +190,30 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(0, 3, 5);
int64_t result = dice(0, 3);
// dice(0) should return negative error code, pass it through
return accept(0, 0, result);
}
)[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, 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, 0x19U, 0x01U, 0x17U, 0x00U, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x41U,
0x00U, 0x41U, 0x03U, 0x41U, 0x05U, 0x10U, 0x01U, 0x10U, 0x02U,
0x0BU,
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,
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, 0x00U,
0x41U, 0x03U, 0x10U, 0x01U, 0x10U, 0x02U, 0x0BU,
}},
/* ==== WASM: 4 ==== */
@@ -223,31 +221,95 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
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)
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(6, 3, 21);
// requirement unmet: pass the error code through
return accept(0, 0, result);
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)
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)
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)
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);
}
)[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, 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,
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,
0x07U, 0x08U, 0x01U, 0x04U, 0x68U, 0x6FU, 0x6FU, 0x6BU, 0x00U,
0x03U, 0x0AU, 0x19U, 0x01U, 0x17U, 0x00U, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x41U,
0x06U, 0x41U, 0x03U, 0x41U, 0x15U, 0x10U, 0x01U, 0x10U, 0x02U,
0x0BU,
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,
}},
/* ==== WASM: 5 ==== */
@@ -255,30 +317,61 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
extern int64_t entropy_status(uint32_t write_ptr, uint32_t write_len);
#define TOO_LITTLE_ENTROPY (-48)
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(6, 4, 0);
return accept(0, 0, result);
uint8_t status[5];
int64_t age = entropy_status((uint32_t)status, 5);
if (age < 0 || age > 1)
return accept(0, 0, 20);
if (status[0] != 1 || status[1] != 0 || status[2] != 0 ||
status[3] != 0 || status[4] != 0)
return accept(0, 0, 21);
int64_t allowed = dice(6, 1);
if (allowed < 0 || allowed > 5)
return accept(0, 0, 22);
if (dice(6, 2) != TOO_LITTLE_ENTROPY)
return accept(0, 0, 23);
return accept(0, 0, age);
}
)[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, 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, 0x19U, 0x01U, 0x17U, 0x00U, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x41U,
0x06U, 0x41U, 0x04U, 0x41U, 0x00U, 0x10U, 0x01U, 0x10U, 0x02U,
0x0BU,
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,
}},
/* ==== WASM: 6 ==== */
@@ -286,30 +379,29 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t dice(uint32_t sides, uint32_t min_tier);
int64_t hook(uint32_t r)
{
_g(1,1);
int64_t result = dice(6, 3, 0);
int64_t result = dice(6, 4);
return accept(0, 0, result);
}
)[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, 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, 0x19U, 0x01U, 0x17U, 0x00U, 0x41U, 0x01U, 0x41U,
0x01U, 0x10U, 0x00U, 0x1AU, 0x41U, 0x00U, 0x41U, 0x00U, 0x41U,
0x06U, 0x41U, 0x03U, 0x41U, 0x00U, 0x10U, 0x01U, 0x10U, 0x02U,
0x0BU,
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,
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, 0x04U, 0x10U, 0x01U, 0x10U, 0x02U, 0x0BU,
}},
/* ==== WASM: 7 ==== */
@@ -317,8 +409,50 @@ 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 dice(uint32_t sides, uint32_t min_tier, uint32_t min_count);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier, uint32_t min_count);
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);
}
)[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, 0x41U, 0x01U, 0x3FU, 0x01U,
0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x10U, 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,
}},
/* ==== 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 dice(uint32_t sides, uint32_t min_tier);
extern int64_t random(uint32_t write_ptr, uint32_t write_len, uint32_t min_tier);
#define INVALID_ARGUMENT (-7)
int64_t hook(uint32_t r)
@@ -326,25 +460,25 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
_g(1,1);
uint8_t buf[32];
int64_t bad_min_tier = dice(6, 0, 0);
int64_t bad_min_tier = dice(6, 0);
if (bad_min_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_min_tier);
int64_t ok_full_tier = dice(6, 4, 0);
int64_t ok_full_tier = dice(6, 4);
if (ok_full_tier < 0 || ok_full_tier > 5)
return accept(0, 0, ok_full_tier);
int64_t bad_high_tier = dice(6, 5, 0);
int64_t bad_high_tier = dice(6, 5);
if (bad_high_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_high_tier);
int64_t bad_min_count = dice(6, 3, 70000);
if (bad_min_count != INVALID_ARGUMENT)
return accept(0, 0, bad_min_count);
int64_t bad_random_low = random((uint32_t)buf, 32, 0);
if (bad_random_low != INVALID_ARGUMENT)
return accept(0, 0, bad_random_low);
int64_t bad_random_tier = random((uint32_t)buf, 32, 5, 0);
if (bad_random_tier != INVALID_ARGUMENT)
return accept(0, 0, bad_random_tier);
int64_t bad_random_high = random((uint32_t)buf, 32, 5);
if (bad_random_high != INVALID_ARGUMENT)
return accept(0, 0, bad_random_high);
// Sentinel distinct from any dice (0..5) / random result and
// from INVALID_ARGUMENT, so a regression that lets a bad
@@ -354,39 +488,38 @@ inline std::map<std::string, std::vector<uint8_t>> consensusentropy_test_wasm =
)[test.hook]",
{
0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U,
0x22U, 0x05U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x03U, 0x7FU, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU,
0x7FU, 0x7EU, 0x01U, 0x7EU, 0x60U, 0x04U, 0x7FU, 0x7FU, 0x7FU,
0x7FU, 0x01U, 0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U,
0x2FU, 0x04U, 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,
0x65U, 0x6EU, 0x76U, 0x06U, 0x72U, 0x61U, 0x6EU, 0x64U, 0x6FU,
0x6DU, 0x00U, 0x03U, 0x03U, 0x02U, 0x01U, 0x04U, 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, 0xB3U, 0x01U, 0x01U,
0xB0U, 0x01U, 0x01U, 0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x20U,
0x6BU, 0x22U, 0x00U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U,
0x10U, 0x00U, 0x1AU, 0x02U, 0x7EU, 0x41U, 0x06U, 0x41U, 0x00U,
0x41U, 0x00U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U,
0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U,
0x02U, 0x0CU, 0x01U, 0x0BU, 0x41U, 0x06U, 0x41U, 0x04U, 0x41U,
0x00U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x06U, 0x5AU, 0x04U,
0x20U, 0x05U, 0x60U, 0x02U, 0x7FU, 0x7FU, 0x01U, 0x7FU, 0x60U,
0x02U, 0x7FU, 0x7FU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU,
0x7EU, 0x01U, 0x7EU, 0x60U, 0x03U, 0x7FU, 0x7FU, 0x7FU, 0x01U,
0x7EU, 0x60U, 0x01U, 0x7FU, 0x01U, 0x7EU, 0x02U, 0x2FU, 0x04U,
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, 0x65U, 0x6EU,
0x76U, 0x06U, 0x72U, 0x61U, 0x6EU, 0x64U, 0x6FU, 0x6DU, 0x00U,
0x03U, 0x03U, 0x02U, 0x01U, 0x04U, 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, 0xA9U, 0x01U, 0x01U, 0xA6U, 0x01U,
0x01U, 0x01U, 0x7EU, 0x23U, 0x00U, 0x41U, 0x20U, 0x6BU, 0x22U,
0x00U, 0x24U, 0x00U, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U,
0x1AU, 0x02U, 0x7EU, 0x41U, 0x06U, 0x41U, 0x00U, 0x10U, 0x01U,
0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U, 0x40U, 0x41U, 0x00U,
0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U, 0x0CU, 0x01U, 0x0BU,
0x41U, 0x06U, 0x41U, 0x04U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U,
0x06U, 0x5AU, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U,
0x01U, 0x10U, 0x02U, 0x0CU, 0x01U, 0x0BU, 0x41U, 0x06U, 0x41U,
0x05U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U,
0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U,
0x0CU, 0x01U, 0x0BU, 0x41U, 0x06U, 0x41U, 0x05U, 0x41U, 0x00U,
0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U, 0x40U,
0x0CU, 0x01U, 0x0BU, 0x20U, 0x00U, 0x41U, 0x20U, 0x41U, 0x00U,
0x10U, 0x03U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U, 0x40U,
0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U, 0x0CU,
0x01U, 0x0BU, 0x41U, 0x06U, 0x41U, 0x03U, 0x41U, 0xF0U, 0xA2U,
0x04U, 0x10U, 0x01U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U,
0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U,
0x0CU, 0x01U, 0x0BU, 0x20U, 0x00U, 0x41U, 0x20U, 0x41U, 0x05U,
0x41U, 0x00U, 0x10U, 0x03U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U,
0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U,
0x02U, 0x0CU, 0x01U, 0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U,
0x2AU, 0x10U, 0x02U, 0x0BU, 0x21U, 0x01U, 0x20U, 0x00U, 0x41U,
0x20U, 0x6AU, 0x24U, 0x00U, 0x20U, 0x01U, 0x0BU,
0x01U, 0x0BU, 0x20U, 0x00U, 0x41U, 0x20U, 0x41U, 0x05U, 0x10U,
0x03U, 0x22U, 0x01U, 0x42U, 0x79U, 0x52U, 0x04U, 0x40U, 0x41U,
0x00U, 0x41U, 0x00U, 0x20U, 0x01U, 0x10U, 0x02U, 0x0CU, 0x01U,
0x0BU, 0x41U, 0x00U, 0x41U, 0x00U, 0x42U, 0x2AU, 0x10U, 0x02U,
0x0BU, 0x21U, 0x01U, 0x20U, 0x00U, 0x41U, 0x20U, 0x6AU, 0x24U,
0x00U, 0x20U, 0x01U, 0x0BU,
}},
};

View File

@@ -127,14 +127,17 @@ dependency on the set that carries the pseudo-tx).
pre-injection set hash.
**INV-6 — Bounded, opt-in entropy quality.**
Hooks state `min_tier` / `min_count` explicitly (no hidden network default).
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
the requirement; otherwise the call **fails closed** (`TOO_LITTLE_ENTROPY`). A
hook never silently receives weaker-than-requested entropy. Draws are also
domain-separated by the hook execution role that can share a transaction and
hook hash: strong vs weak, callback vs direct dispatch, and hook chain
position.
*Enforced:* `fairRng` gate.
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.
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
dispatch, and hook chain position.
*Enforced:* `fairRng` tier/freshness gate and metadata-only `entropy_status`.
**INV-7 — Inert when un-amended.**
With `featureConsensusEntropy` off, no RNG sidecar state is consensus-visible and

View File

@@ -375,20 +375,27 @@ digest/tier/count/denominator tuple. This does not make the bitmap
non-critical: any disagreement in a ledger-written field is a ledger
disagreement. It only keeps transaction ordering semantically tied to the
entropy value and quality labels rather than to the accountability label. Hooks
state their own requirements via the required
`min_tier`/`min_count` arguments to `dice()`/`random()`: a hook that demands
validator-tier entropy fails closed with `TOO_LITTLE_ENTROPY` on fallback
ledgers, while a hook that opts into fallback-grade randomness must do so
explicitly at the call site. Valid `min_tier` values are the stored entropy
tiers 1..4; `min_count` must fit the on-ledger `EntropyCount` UINT16 field.
Invalid requirements return `INVALID_ARGUMENT`, while valid-but-unmet
requirements return `TOO_LITTLE_ENTROPY`.
state their class requirement through the mandatory `min_tier` argument to
`dice()`/`random()`: a hook that demands validator-tier entropy fails closed
with `TOO_LITTLE_ENTROPY` on fallback ledgers, while a hook that opts into
fallback-grade randomness must do so explicitly at the call site. Valid
`min_tier` values are the stored entropy tiers 1..4; invalid requirements return
`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,
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
`0/0`.
Open-ledger hook execution is provisional. During speculative open-ledger
execution, `dice()`/`random()` can only use the previous 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.
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.
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

@@ -16,6 +16,7 @@
#include <xrpl/protocol/st.h>
#include <xrpl/protocol/tokens.h>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <array>
#include <limits>
#include <memory>
#include <optional>
@@ -4059,24 +4060,59 @@ DEFINE_HOOK_FUNCTION(
//@@end xport-impl
inline bool
invalidEntropyRequirement(uint32_t minTier, uint32_t minCount)
invalidEntropyRequirement(uint32_t minTier)
{
return minTier < entropyTierConsensusFallback ||
minTier > entropyTierValidatorFull ||
minCount > std::numeric_limits<std::uint16_t>::max();
minTier > entropyTierValidatorFull;
}
struct EntropySnapshot
{
std::shared_ptr<SLE> sle;
std::uint32_t age;
std::uint8_t tier;
std::uint16_t count;
std::uint16_t denominator;
};
inline std::variant<EntropySnapshot, hook_api::hook_return_code>
readEntropySnapshot(ApplyView& view)
{
auto sle = view.peek(ripple::keylet::consensusEntropy());
if (!sle)
return hook_api::hook_return_code::DOESNT_EXIST;
if (!sle->isFieldPresent(sfDigest) ||
!sle->isFieldPresent(sfLedgerSequence) ||
!sle->isFieldPresent(sfEntropyTier) ||
!sle->isFieldPresent(sfEntropyCount) ||
!sle->isFieldPresent(sfEntropyDenominator) ||
!sle->isFieldPresent(sfEntropyContributors))
return hook_api::hook_return_code::INTERNAL_ERROR;
auto const seq = view.info().seq;
auto const entropySeq = sle->getFieldU32(sfLedgerSequence);
if (entropySeq > seq)
return hook_api::hook_return_code::INTERNAL_ERROR;
return EntropySnapshot{
sle,
seq - entropySeq,
sle->getFieldU8(sfEntropyTier),
sle->getFieldU16(sfEntropyCount),
sle->getFieldU16(sfEntropyDenominator)};
}
// byteCount must be a multiple of 32.
// minTier/minCount are the CALLER'S stated requirements (required hook API
// arguments — there is deliberately no network-wide default): entropy is
// usable iff tier >= minTier && count >= minCount, in addition to freshness.
// minTier is the CALLER'S stated class requirement (a required hook API
// argument — there is deliberately no network-wide default). Count and
// denominator policy is available separately through entropy_status().
inline std::vector<uint8_t>
fairRng(
ApplyContext& applyCtx,
hook::HookResult& hr,
uint32_t byteCount,
uint32_t minTier,
uint32_t minCount)
uint32_t minTier)
{
if (byteCount > 512)
byteCount = 512;
@@ -4088,28 +4124,17 @@ fairRng(
return {};
auto& view = applyCtx.view();
auto const sleEntropy = view.peek(ripple::keylet::consensusEntropy());
auto const seq = view.info().seq;
auto const entropySeq =
sleEntropy ? sleEntropy->getFieldU32(sfLedgerSequence) : 0u;
auto snapshot = readEntropySnapshot(view);
if (!std::holds_alternative<EntropySnapshot>(snapshot))
return {};
auto const& entropy = std::get<EntropySnapshot>(snapshot);
// Open-ledger hook execution is provisional and can only see the previous
// ledger's finalized entropy. Final buildLCL execution sees the current
// ledger's entropy pseudo-tx after it updates this SLE. That open-vs-final
// skew is inherent to speculative execution; callers that need final
// entropy must treat open-ledger dice/random results as previews.
// Defensive: sfEntropyTier is soeREQUIRED, so any entry this code wrote
// carries it. A missing field can only come from a pre-tier-3 persisted
// entry; treat that as tier 0 (none) so the requirement check fails closed.
auto const entropyTier =
sleEntropy && sleEntropy->isFieldPresent(sfEntropyTier)
? sleEntropy->getFieldU8(sfEntropyTier)
: std::uint8_t{0};
if (!sleEntropy || entropySeq > seq || (seq - entropySeq) > 1 ||
entropyTier < minTier ||
sleEntropy->getFieldU16(sfEntropyCount) < minCount)
if (entropy.age > 1 || entropy.tier < minTier)
return {};
// we'll generate bytes in lots of 32
@@ -4130,7 +4155,7 @@ fairRng(
hr.hookChainPosition,
hr.isStrong ? std::string("strong") : std::string("weak"),
hr.isCallback ? std::string("callback") : std::string("direct"),
sleEntropy->getFieldH256(sfDigest),
entropy.sle->getFieldH256(sfDigest),
hr.rngCallCounter++);
std::vector<uint8_t> bytesOut;
@@ -4151,22 +4176,17 @@ fairRng(
return bytesOut;
}
DEFINE_HOOK_FUNCTION(
int64_t,
dice,
uint32_t sides,
uint32_t min_tier,
uint32_t min_count)
DEFINE_HOOK_FUNCTION(int64_t, dice, uint32_t sides, uint32_t min_tier)
{
HOOK_SETUP();
if (sides == 0)
return INVALID_ARGUMENT;
if (invalidEntropyRequirement(min_tier, min_count))
if (invalidEntropyRequirement(min_tier))
return INVALID_ARGUMENT;
auto vec = fairRng(applyCtx, hookCtx.result, 32, min_tier, min_count);
auto vec = fairRng(applyCtx, hookCtx.result, 32, min_tier);
if (vec.empty())
return TOO_LITTLE_ENTROPY;
@@ -4206,8 +4226,7 @@ DEFINE_HOOK_FUNCTION(
random,
uint32_t write_ptr,
uint32_t write_len,
uint32_t min_tier,
uint32_t min_count)
uint32_t min_tier)
{
HOOK_SETUP();
@@ -4233,10 +4252,10 @@ DEFINE_HOOK_FUNCTION(
if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length))
return OUT_OF_BOUNDS;
if (invalidEntropyRequirement(min_tier, min_count))
if (invalidEntropyRequirement(min_tier))
return INVALID_ARGUMENT;
auto vec = fairRng(applyCtx, hookCtx.result, required, min_tier, min_count);
auto vec = fairRng(applyCtx, hookCtx.result, required, min_tier);
if (vec.empty())
return TOO_LITTLE_ENTROPY;
@@ -4246,6 +4265,42 @@ DEFINE_HOOK_FUNCTION(
HOOK_TEARDOWN();
}
DEFINE_HOOK_FUNCTION(
int64_t,
entropy_status,
uint32_t write_ptr,
uint32_t write_len)
{
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;
HOOK_TEARDOWN();
}
/*
DEFINE_HOOK_FUNCTION(