mirror of
https://github.com/Xahau/xahaud.git
synced 2026-07-28 17:40:10 +00:00
test(entropy): pin digest and manifest rotation
This commit is contained in:
@@ -299,13 +299,33 @@ singleCanonicalTx(CanonicalTXSet const& txs)
|
||||
return txs.begin()->second;
|
||||
}
|
||||
|
||||
uint256
|
||||
expectedEntropy(std::vector<std::pair<PublicKey, uint256>> contributions)
|
||||
{
|
||||
std::sort(
|
||||
contributions.begin(),
|
||||
contributions.end(),
|
||||
[](auto const& a, auto const& b) {
|
||||
if (a.first.slice() < b.first.slice())
|
||||
return true;
|
||||
if (b.first.slice() < a.first.slice())
|
||||
return false;
|
||||
return a.second < b.second;
|
||||
});
|
||||
|
||||
Serializer s;
|
||||
for (auto const& [key, reveal] : contributions)
|
||||
{
|
||||
s.addVL(key.slice());
|
||||
s.addBitString(reveal);
|
||||
}
|
||||
return sha512Half(s.slice());
|
||||
}
|
||||
|
||||
uint256
|
||||
expectedEntropy(PublicKey const& key, uint256 const& reveal)
|
||||
{
|
||||
Serializer s;
|
||||
s.addVL(key.slice());
|
||||
s.addBitString(reveal);
|
||||
return sha512Half(s.slice());
|
||||
return expectedEntropy({{key, reveal}});
|
||||
}
|
||||
|
||||
Buffer
|
||||
@@ -1618,6 +1638,79 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(tx->getFieldU8(sfEntropyTier) == entropyTierValidatorFull);
|
||||
}
|
||||
|
||||
void
|
||||
testOnPreBuildCanonicalizesMultiContributorEntropy()
|
||||
{
|
||||
testcase("onPreBuild canonicalizes multi-contributor entropy");
|
||||
|
||||
using namespace jtx;
|
||||
Env env{
|
||||
*this,
|
||||
envconfig(validator, ""),
|
||||
supported_amendments() | featureConsensusEntropy,
|
||||
nullptr};
|
||||
forceNonStandalone(env.app());
|
||||
|
||||
std::vector<std::pair<PublicKey, SecretKey>> validators;
|
||||
validators.push_back(randomKeyPair(KeyType::secp256k1));
|
||||
validators.push_back(randomKeyPair(KeyType::secp256k1));
|
||||
std::vector<PublicKey> activeKeys{
|
||||
validators[0].first, validators[1].first};
|
||||
auto const viewLedger = makeUNLReportLedger(env, activeKeys);
|
||||
auto const anchor = env.app().getLedgerMaster().getClosedLedger();
|
||||
auto const seq = anchor->info().seq + 1;
|
||||
auto const closeTime = NetClock::time_point{NetClock::duration{322}};
|
||||
auto const txSetHash = makeHash("multi-contributor-entropy-txset");
|
||||
std::vector<uint256> const reveals{
|
||||
makeHash("multi-contributor-reveal-a"),
|
||||
makeHash("multi-contributor-reveal-b")};
|
||||
|
||||
ConsensusExtensions ce{env.app(), activeNoopJournal()};
|
||||
ce.onRoundStart(RCLCxLedger{anchor}, {});
|
||||
ce.cacheUNLReport(viewLedger);
|
||||
ce.setRngEnabledThisRound(true);
|
||||
|
||||
// Deliberately harvest in reverse signing-key order. The accepted
|
||||
// entropy bytes must depend on canonical key/reveal order, not packet
|
||||
// arrival or SHAMap traversal order.
|
||||
for (std::size_t i : {std::size_t{1}, std::size_t{0}})
|
||||
{
|
||||
harvestCommitReveal(
|
||||
ce,
|
||||
calcNodeID(validators[i].first),
|
||||
validators[i].first,
|
||||
validators[i].second,
|
||||
txSetHash,
|
||||
seq,
|
||||
closeTime,
|
||||
anchor->info().hash,
|
||||
reveals[i]);
|
||||
}
|
||||
BEAST_EXPECT(ce.hasQuorumOfCommits());
|
||||
BEAST_EXPECT(ce.hasMinimumReveals());
|
||||
|
||||
auto const entropySetHash = ce.buildEntropySet(seq);
|
||||
ce.acceptEntropySet(entropySetHash);
|
||||
CanonicalTXSet txs{makeHash("multi-contributor-entropy-salt")};
|
||||
ce.onPreBuild(txs, seq, txSetHash);
|
||||
|
||||
auto const tx = singleCanonicalTx(txs);
|
||||
BEAST_EXPECT(tx);
|
||||
if (!tx)
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(tx->getTxnType() == ttCONSENSUS_ENTROPY);
|
||||
BEAST_EXPECT(
|
||||
tx->getFieldH256(sfDigest) ==
|
||||
expectedEntropy(
|
||||
{{validators[0].first, reveals[0]},
|
||||
{validators[1].first, reveals[1]}}));
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyCount) == 2);
|
||||
BEAST_EXPECT(tx->getFieldU16(sfEntropyDenominator) == 2);
|
||||
BEAST_EXPECT(tx->getFieldVL(sfEntropyContributors) == Blob{0x03});
|
||||
BEAST_EXPECT(tx->getFieldU8(sfEntropyTier) == entropyTierValidatorFull);
|
||||
}
|
||||
|
||||
void
|
||||
testOnPreBuildAcceptedEntropySetOverridesLocalFailureFlag()
|
||||
{
|
||||
@@ -2416,6 +2509,117 @@ class ConsensusExtensions_test : public beast::unit_test::suite
|
||||
proposalFirst.buildEntropySet(seq));
|
||||
}
|
||||
|
||||
void
|
||||
testRngManifestRotationDropsPinnedContributor()
|
||||
{
|
||||
testcase("RNG manifest rotation drops pinned contributor");
|
||||
|
||||
using namespace jtx;
|
||||
auto const masterSeed = randomSeed();
|
||||
Env env{
|
||||
*this,
|
||||
envconfig(validator, toBase58(masterSeed)),
|
||||
supported_amendments(),
|
||||
nullptr};
|
||||
auto const anchor = env.app().getLedgerMaster().getClosedLedger();
|
||||
auto const masterSecret =
|
||||
generateSecretKey(KeyType::secp256k1, masterSeed);
|
||||
auto const masterKey =
|
||||
derivePublicKey(KeyType::secp256k1, masterSecret);
|
||||
auto const signingSecret1 =
|
||||
generateSecretKey(KeyType::secp256k1, randomSeed());
|
||||
auto const signingSecret2 =
|
||||
generateSecretKey(KeyType::secp256k1, randomSeed());
|
||||
auto const signingKey1 =
|
||||
derivePublicKey(KeyType::secp256k1, signingSecret1);
|
||||
auto const signingKey2 =
|
||||
derivePublicKey(KeyType::secp256k1, signingSecret2);
|
||||
auto const nodeId = calcNodeID(masterKey);
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.app().validatorManifests().applyManifest(
|
||||
makeValidatorManifest(masterSecret, signingSecret1, 1)) ==
|
||||
ManifestDisposition::accepted);
|
||||
|
||||
auto const viewLedger =
|
||||
makeUNLReportLedger(env, std::vector<PublicKey>{masterKey});
|
||||
ConsensusExtensions ce{env.app(), activeNoopJournal()};
|
||||
ce.cacheUNLReport(viewLedger);
|
||||
|
||||
auto const seq = anchor->info().seq + 1;
|
||||
auto const closeTime = NetClock::time_point{NetClock::duration{656}};
|
||||
auto const txSetHash = makeHash("manifest-rotation-txset");
|
||||
auto const reveal = makeHash("manifest-rotation-reveal");
|
||||
auto const commitment = sha512Half(reveal, signingKey1, seq);
|
||||
|
||||
ExtendedPosition commitPos{txSetHash};
|
||||
commitPos.myCommitment = commitment;
|
||||
auto const commitSig = signPosition(
|
||||
signingKey1,
|
||||
signingSecret1,
|
||||
commitPos,
|
||||
0,
|
||||
closeTime,
|
||||
anchor->info().hash);
|
||||
ce.harvestRngData(
|
||||
nodeId,
|
||||
signingKey1,
|
||||
commitPos,
|
||||
0,
|
||||
closeTime,
|
||||
anchor->info().hash,
|
||||
Slice(commitSig.data(), commitSig.size()));
|
||||
BEAST_EXPECT(ce.pendingCommitCount() == 1);
|
||||
BEAST_EXPECT(ce.hasQuorumOfCommits());
|
||||
|
||||
BEAST_EXPECT(
|
||||
env.app().validatorManifests().applyManifest(
|
||||
makeValidatorManifest(masterSecret, signingSecret2, 2)) ==
|
||||
ManifestDisposition::accepted);
|
||||
|
||||
ExtendedPosition revealPos{txSetHash};
|
||||
revealPos.myReveal = reveal;
|
||||
auto const retiredKeySig = signPosition(
|
||||
signingKey1,
|
||||
signingSecret1,
|
||||
revealPos,
|
||||
1,
|
||||
closeTime,
|
||||
anchor->info().hash);
|
||||
ce.harvestRngData(
|
||||
nodeId,
|
||||
signingKey1,
|
||||
revealPos,
|
||||
1,
|
||||
closeTime,
|
||||
anchor->info().hash,
|
||||
Slice(retiredKeySig.data(), retiredKeySig.size()));
|
||||
|
||||
auto const substitutedKeySig = signPosition(
|
||||
signingKey2,
|
||||
signingSecret2,
|
||||
revealPos,
|
||||
2,
|
||||
closeTime,
|
||||
anchor->info().hash);
|
||||
ce.harvestRngData(
|
||||
nodeId,
|
||||
signingKey2,
|
||||
revealPos,
|
||||
2,
|
||||
closeTime,
|
||||
anchor->info().hash,
|
||||
Slice(substitutedKeySig.data(), substitutedKeySig.size()));
|
||||
|
||||
BEAST_EXPECT(ce.pendingRevealCount() == 0);
|
||||
BEAST_EXPECT(!ce.hasMinimumReveals());
|
||||
auto const entropyHash = ce.buildEntropySet(seq);
|
||||
auto const entropySet =
|
||||
env.app().getInboundTransactions().getSet(entropyHash, false);
|
||||
BEAST_EXPECT(entropySet);
|
||||
BEAST_EXPECT(entropySet && entropySet->getHash().isZero());
|
||||
}
|
||||
|
||||
void
|
||||
testExportCollectorBuildsAttributedUnion()
|
||||
{
|
||||
@@ -4577,6 +4781,7 @@ public:
|
||||
testOnPreBuildInjectsZeroEntropyFallback();
|
||||
testTxnOrderingSaltExtendsLegacySalt();
|
||||
testOnPreBuildInjectsEntropySetEntropy();
|
||||
testOnPreBuildCanonicalizesMultiContributorEntropy();
|
||||
testOnPreBuildAcceptedEntropySetOverridesLocalFailureFlag();
|
||||
testOnPreBuildRejectsForgedEntropyContributorAttribution();
|
||||
testOnPreBuildTier2ParticipantAligned();
|
||||
@@ -4586,6 +4791,7 @@ public:
|
||||
testProposalPrecheckUsesExportShareRelayLimits();
|
||||
testHarvestRngDataReplacementAndRejection();
|
||||
testRngManifestArrivalDoesNotRetargetProofedCommit();
|
||||
testRngManifestRotationDropsPinnedContributor();
|
||||
testExportCollectorBuildsAttributedUnion();
|
||||
testExportSidecarCandidateDeadline();
|
||||
testTransactionAcquireRejectsSidecarWireNodes();
|
||||
|
||||
Reference in New Issue
Block a user