anchor: the corpus carries AIP-22 stage 5 (headers sealed by the proposer's post-quantum seal, no ECDSA committed seals) and the stage-3b correction (a commit's hybrid seal judged with the v3 epoch's key)
This commit is contained in:
parent
ab90cc5b06
commit
344ba39314
11
LICENSING.md
11
LICENSING.md
@ -123,7 +123,7 @@ the section 4(b) notice of changed files), and each keeps its original notice pe
|
|||||||
|
|
||||||
<!-- GENERAT: lista de mai jos se rescrie de unealta de licentiere din depozitul de constructie -->
|
<!-- GENERAT: lista de mai jos se rescrie de unealta de licentiere din depozitul de constructie -->
|
||||||
|
|
||||||
## anchor/: every file that carries someone else's copyright (generated 2026-09-23)
|
## anchor/: every file that carries someone else's copyright (generated 2026-09-24)
|
||||||
|
|
||||||
This list is **generated from the files this package actually ships**, not written by hand: the
|
This list is **generated from the files this package actually ships**, not written by hand: the
|
||||||
previous hand-written table fell behind the tree on 2026-09-12 and seven upstream files went
|
previous hand-written table fell behind the tree on 2026-09-12 and seven upstream files went
|
||||||
@ -132,9 +132,9 @@ repository, and the publisher refuses to ship a package whose list disagrees wit
|
|||||||
You do not need that tool to check the list: every row below names a file in this package, and
|
You do not need that tool to check the list: every row below names a file in this package, and
|
||||||
`grep -i copyright` on it shows the same line.
|
`grep -i copyright` on it shows the same line.
|
||||||
|
|
||||||
Measured 2026-09-23 over `anchor/`: **198 files**. **90** carry someone
|
Measured 2026-09-24 over `anchor/`: **202 files**. **90** carry someone
|
||||||
else's copyright alone, **54** carry a shared notice naming both upstream and us, and **11** name only
|
else's copyright alone, **57** carry a shared notice naming both upstream and us, and **11** name only
|
||||||
`contributors to the AERE Network`; 43 carry no copyright line at all.
|
`contributors to the AERE Network`; 44 carry no copyright line at all.
|
||||||
The first two groups are the table below. A shared notice is listed too: those are precisely the
|
The first two groups are the table below. A shared notice is listed too: those are precisely the
|
||||||
upstream files we changed, so section 4(b) has something to say about each of them.
|
upstream files we changed, so section 4(b) has something to say about each of them.
|
||||||
Every file below is Apache-2.0 material redistributed under section 4: it keeps its original
|
Every file below is Apache-2.0 material redistributed under section 4: it keeps its original
|
||||||
@ -150,6 +150,7 @@ notice (4(c)), and where we changed it the words **modified by Aere Network** ar
|
|||||||
| `anchor/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/consensus/common/build.gradle` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/consensus/common/build.gradle` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
|
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
@ -268,6 +269,7 @@ notice (4(c)), and where we changed it the words **modified by Aere Network** ar
|
|||||||
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
|
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqRegistryBindingRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqRegistryBindingRule.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealLogThrottleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealLogThrottleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealValidationRuleRetirementTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealValidationRuleRetirementTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
@ -279,6 +281,7 @@ notice (4(c)), and where we changed it the words **modified by Aere Network** ar
|
|||||||
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRuleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRuleTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqForkGateFeedTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqForkGateFeedTest.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
|
| `anchor/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java` | `Copyright contributors to Besu / AERE Network.` | upstream, **modified by Aere Network** |
|
||||||
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java` | `Copyright ConsenSys AG.` | upstream |
|
| `anchor/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java` | `Copyright ConsenSys AG.` | upstream |
|
||||||
|
|||||||
@ -5,15 +5,16 @@ computed them, and the same 276 files once produced two different aggregates her
|
|||||||
|
|
||||||
329e4e2f93143cc8af092f0a951fa23619c12c4ec1793b053654f8e26e088da0 app/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
|
329e4e2f93143cc8af092f0a951fa23619c12c4ec1793b053654f8e26e088da0 app/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
|
||||||
3f289d74bb1878280a99742320d7a27c19146a2f19cdf6a0857f2aad4361ad5e app/src/main/java/org/hyperledger/besu/cli/options/AerePqEmergencyOptions.java
|
3f289d74bb1878280a99742320d7a27c19146a2f19cdf6a0857f2aad4361ad5e app/src/main/java/org/hyperledger/besu/cli/options/AerePqEmergencyOptions.java
|
||||||
6bc5a9ae4dbeda9a26e3157cb61c938cc306c859ee597526107199e0c88166ae app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
|
1615ecaf19f75af3e91c27c029177bf0faf51839f5589878b8c3562371299865 app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
|
||||||
129493150c0b13e8020bdf50c0c1ae07268d4f73a69a821b303bf4894451fa5e app/src/test/java/org/hyperledger/besu/cli/options/AerePqEmergencyOptionsTest.java
|
129493150c0b13e8020bdf50c0c1ae07268d4f73a69a821b303bf4894451fa5e app/src/test/java/org/hyperledger/besu/cli/options/AerePqEmergencyOptionsTest.java
|
||||||
728c29c299edf6011f2c2798cbe930a043112e0e06b924e7edb54284ca44a758 config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
|
728c29c299edf6011f2c2798cbe930a043112e0e06b924e7edb54284ca44a758 config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
|
||||||
a3b93f2602e9755d91358c9b3f473235f4d40ae23d912f797cdb03ed7e1348f9 consensus/common/build.gradle
|
a3b93f2602e9755d91358c9b3f473235f4d40ae23d912f797cdb03ed7e1348f9 consensus/common/build.gradle
|
||||||
68d635954ef33448a661256f50642eeeff2f7bb5874a54ffc2e64193c83fe391 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AereAnchorProposalDelay.java
|
68d635954ef33448a661256f50642eeeff2f7bb5874a54ffc2e64193c83fe391 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AereAnchorProposalDelay.java
|
||||||
ed2e46aaf2e74661ad831dc31df0b6f795f28f4a94496a7d226feedf2a770fde consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java
|
ed2e46aaf2e74661ad831dc31df0b6f795f28f4a94496a7d226feedf2a770fde consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java
|
||||||
|
290d94bb50f23666b131ec19659982c15f1e6cbaf8f8bfe128df9acb2f2f79fd consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java
|
||||||
348135b2e4d0a6a8ce91489c94fbfa77da2536196c13823676b33ea9d08f8fb9 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java
|
348135b2e4d0a6a8ce91489c94fbfa77da2536196c13823676b33ea9d08f8fb9 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java
|
||||||
c28e3bf26d68ab7b764f6381bc678d5f7b60bfa7d02796e3ac34521c5c5bc6ed consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java
|
c531f9e719c48d3d5da2800fdef4506bd167af4dad4e25af96b4353a4390067d consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java
|
||||||
2e84bb48611d3da7c46bcd4018ae746cbf785dfcb16f495674a0f6d542e71e8e consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java
|
9fcf44789ddf6d5b80be7d44f0bec58af6ba8316733d49817e890f0e17b71223 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java
|
||||||
70fa067469a084ac225765da6a01619dec51130acc255b7194a1ad274d327141 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java
|
70fa067469a084ac225765da6a01619dec51130acc255b7194a1ad274d327141 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BlockTimer.java
|
||||||
3ab425b5b5f7d7c2199065a3905623d5c1c4c5922d892c6c4275415b0c69a2b4 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSeal.java
|
3ab425b5b5f7d7c2199065a3905623d5c1c4c5922d892c6c4275415b0c69a2b4 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSeal.java
|
||||||
db1e80115ce59c8281fbf046cb37e153a49fe017c1675afd200e8125d3f6b538 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealScheme.java
|
db1e80115ce59c8281fbf046cb37e153a49fe017c1675afd200e8125d3f6b538 consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealScheme.java
|
||||||
@ -121,12 +122,12 @@ d365bad33f3aee4597c630071ca1f727ddd8bb6ab6515fccbce0a07842037adf consensus/qbft
|
|||||||
e19b12e6db33cfb9bb1c2f22e321815130dfbcc5730bb505320f08a45a371093 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java
|
e19b12e6db33cfb9bb1c2f22e321815130dfbcc5730bb505320f08a45a371093 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java
|
||||||
51eddf3cb8e37489abd35e6f4c36294a13ec76337bbbbfd54c07584085dceaac consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftBlockHeightManager.java
|
51eddf3cb8e37489abd35e6f4c36294a13ec76337bbbbfd54c07584085dceaac consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftBlockHeightManager.java
|
||||||
0a450699c702104dcef6bf8912b4d180f05d647288c75a9f78da6af3bd3d90f1 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftController.java
|
0a450699c702104dcef6bf8912b4d180f05d647288c75a9f78da6af3bd3d90f1 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftController.java
|
||||||
9c7e2698016f8cf19d3b6d78c6256ad5e8eabec06d9a877eee4e8c2dde423839 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java
|
6a34f5e87b5c5c629f1f08d0dc88c336a3ab3c0ba8797d3b0ee477246f10f4c6 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java
|
||||||
00c8204e4b44acd469973d6bbc07992382fdd2d692f079a55de76f944a7432f8 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java
|
4494f33ca028b60eaa093e6c24dd13706461e228f7f5a29e40e8917d1ef3b373 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java
|
||||||
601b0cccf32f8ebe327c7581e0e9b3f9f67de49d9fb646326d6b49ef2138c641 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java
|
eadc4a14e2f0fdf7c1d9667d67c59f709af7a86fccf51923de0248b73e223c21 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java
|
||||||
4c0811d182f5252bdde31560f514a518db58c8d0c82c748a6c0f5d68472470c6 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java
|
4c0811d182f5252bdde31560f514a518db58c8d0c82c748a6c0f5d68472470c6 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java
|
||||||
a40621537e2b2024ab5586c9001f96fd81ffe3c3d5ce1e867a07a77584cf2173 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/MessageValidator.java
|
a40621537e2b2024ab5586c9001f96fd81ffe3c3d5ce1e867a07a77584cf2173 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/MessageValidator.java
|
||||||
22c73f9cdfe5626347f34f173b7b60312e00970ccba49862686051b2dd84fe36 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java
|
60c0c68de44d277456f84452d2acaf93a4a239728f65af2fb55be49bbe417123 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java
|
||||||
bacf232d9f3a9163ce6df3091c5e9161efcb26a73aecaead910e6952ee27bf86 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java
|
bacf232d9f3a9163ce6df3091c5e9161efcb26a73aecaead910e6952ee27bf86 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java
|
||||||
5ab52433f770537e838b15e23abfc7b24eaa733ea26577451d5e32226d617879 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareEnforcement.java
|
5ab52433f770537e838b15e23abfc7b24eaa733ea26577451d5e32226d617879 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareEnforcement.java
|
||||||
742611d52091a6c138cdff60c5f7f0bfb2fc33e567f520f3e86878fc086e77d9 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqProposalEnforcement.java
|
742611d52091a6c138cdff60c5f7f0bfb2fc33e567f520f3e86878fc086e77d9 consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqProposalEnforcement.java
|
||||||
@ -153,6 +154,7 @@ c1b31f5c357a1966cdd1e963369a1eb3078a381679c6adc8fb5206f754eca84b consensus/qbft
|
|||||||
c0853ed97c53d54951e25ad6d0b70c0299dedd859cc7c44da64e3751d0e0de33 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitPlumbingTest.java
|
c0853ed97c53d54951e25ad6d0b70c0299dedd859cc7c44da64e3751d0e0de33 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitPlumbingTest.java
|
||||||
01cefcd272ca54e1ebd37f606ec87cc884918c1d72289347a7c234cdca2840ff consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java
|
01cefcd272ca54e1ebd37f606ec87cc884918c1d72289347a7c234cdca2840ff consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java
|
||||||
52e75fd98b9d2ab3d6f87fc8ea1a638321e70d5eec5f14db615ee13755ba1287 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java
|
52e75fd98b9d2ab3d6f87fc8ea1a638321e70d5eec5f14db615ee13755ba1287 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java
|
||||||
|
1834ed0950cdac7620091315e1dfd60f61cecd10def1196c69bf8a22db08f943 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridRotationV3Test.java
|
||||||
bca46ca91803c89eb18d670c740edb4e47ef0bca53a9b4b82ce0c8446e0d6c0e consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqMessageAgilityTest.java
|
bca46ca91803c89eb18d670c740edb4e47ef0bca53a9b4b82ce0c8446e0d6c0e consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqMessageAgilityTest.java
|
||||||
5a47d247d7bcda77b57f5c906c3cee1af826785416012fafe9cfa49d63671388 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareAgilityTest.java
|
5a47d247d7bcda77b57f5c906c3cee1af826785416012fafe9cfa49d63671388 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareAgilityTest.java
|
||||||
1939e33cc8ea81782e5e17d68e2b59c001e4207ed8ef07acd1d2a166a047dcb3 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareEnforcementTest.java
|
1939e33cc8ea81782e5e17d68e2b59c001e4207ed8ef07acd1d2a166a047dcb3 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqPrepareEnforcementTest.java
|
||||||
@ -163,9 +165,9 @@ dc9f9e862a11f0135d26976176a1d1adac3e84fd5f1e06572d8727262cb3374f consensus/qbft
|
|||||||
2c612868dfdd61d86cadc4ca9b34faacf4e43aca88878feb72ac140e1321ef5d consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/ProposalPayloadValidatorPqWiringTest.java
|
2c612868dfdd61d86cadc4ca9b34faacf4e43aca88878feb72ac140e1321ef5d consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/ProposalPayloadValidatorPqWiringTest.java
|
||||||
83dd971f66ed63103d09db5283240556bf1db1e1925c2a07808c7f59d47433ad consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangeJustificationPqTest.java
|
83dd971f66ed63103d09db5283240556bf1db1e1925c2a07808c7f59d47433ad consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangeJustificationPqTest.java
|
||||||
8f6abb4297df964e934cfeabe51f365478e917b73cd6909f621c9e201e280aa5 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidatorPqWiringTest.java
|
8f6abb4297df964e934cfeabe51f365478e917b73cd6909f621c9e201e280aa5 consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidatorPqWiringTest.java
|
||||||
77568cb12200fd653772cd5bb93f989b012909fdb645d68fa39e8251138ec89e consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java
|
24ee993a415eea57c80fe2c9b839ce20140bfcc580e269e7b1182f71e960c1f0 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java
|
||||||
42b86cd888efe1aa00f9c9477566e353649c609c13a042e514185b57a6674a33 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java
|
65ba290ae597b2217b86f2dd4b9db5faacc6e60ef7e36343a9d724c40b66dd73 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java
|
||||||
103d46eecfc11c97fb59a1e0237a6b5b6d384a43ec45f5028bd225b8bf8e3f6c consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java
|
c870d39a19174328bc394658f4a365a61bd57fe7b0e28e6c5e4aaeb28bb509e5 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java
|
||||||
cfb0aad408ab620f28b09914ece7d8d3d09f9e6bbb503070e28d9ef39c023dee consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactory.java
|
cfb0aad408ab620f28b09914ece7d8d3d09f9e6bbb503070e28d9ef39c023dee consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/blockcreation/QbftBlockCreatorFactory.java
|
||||||
cbec6f4e280250ddefe5c79c453b59a07be6435e6f88d5614e92168c855e4693 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/AereBaseFeeImportRule.java
|
cbec6f4e280250ddefe5c79c453b59a07be6435e6f88d5614e92168c855e4693 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/AereBaseFeeImportRule.java
|
||||||
e4e75c28ff15d058176145b1ae32606bf91b22112d1668a718f1a6ae4add0e71 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealValidationRule.java
|
e4e75c28ff15d058176145b1ae32606bf91b22112d1668a718f1a6ae4add0e71 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/FalconSealValidationRule.java
|
||||||
@ -174,6 +176,7 @@ ab0fcd8722dcb76560f0ef8fda8af9c2b8b6ec9b468326aaf616c853526f7f81 consensus/qbft
|
|||||||
0f348ac4fa519f27f52f631dd290eab6e84980739a09ec767c8ca69e4a4f8f64 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java
|
0f348ac4fa519f27f52f631dd290eab6e84980739a09ec767c8ca69e4a4f8f64 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java
|
||||||
a76a87987050ea6765e2a1f43d8f828ca8aec218c951e395e7acb62465e8b2fd consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java
|
a76a87987050ea6765e2a1f43d8f828ca8aec218c951e395e7acb62465e8b2fd consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java
|
||||||
2add2a7733668e11b50ddd64b05bb37db26a18284a636bbe720c2cb1f6aa28ed consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRule.java
|
2add2a7733668e11b50ddd64b05bb37db26a18284a636bbe720c2cb1f6aa28ed consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRule.java
|
||||||
|
47dd0c84b2f1dba800ec843a0c06f85db86825e06493df13ffca3d5b5a9219dc consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java
|
||||||
8ac99e94c89f61f0281199cf369e282fc4f9fb1a3414a1650766e1d7548e76f5 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqRegistryBindingRule.java
|
8ac99e94c89f61f0281199cf369e282fc4f9fb1a3414a1650766e1d7548e76f5 consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqRegistryBindingRule.java
|
||||||
5dcf5c22389c063e12363b83366b39bd3b2e79f36c68e777fb3ef11080f53ea9 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java
|
5dcf5c22389c063e12363b83366b39bd3b2e79f36c68e777fb3ef11080f53ea9 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java
|
||||||
52b56f157500ae3527b7e55c51786915bb5d6980065668f1e7b8e297bf7125ef consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftAnchorRuleWiringTest.java
|
52b56f157500ae3527b7e55c51786915bb5d6980065668f1e7b8e297bf7125ef consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftAnchorRuleWiringTest.java
|
||||||
@ -189,6 +192,7 @@ a81da71dd34dd111a0da43c22d22345dcc595c084a2c2d36aec1f45d8b762f80 consensus/qbft
|
|||||||
9c4e6a56ecffa70ebb03f769eda8c161d70eed47935488b88cb30c1bafa15a73 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java
|
9c4e6a56ecffa70ebb03f769eda8c161d70eed47935488b88cb30c1bafa15a73 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java
|
||||||
484bc74ff3d1e52d25631264d83af70c2b695e973c7360b0f3db471ab5c6613a consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRuleTest.java
|
484bc74ff3d1e52d25631264d83af70c2b695e973c7360b0f3db471ab5c6613a consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqEmergencyShoutRuleTest.java
|
||||||
49c3fd97e7985966531928af173b6f38ac48ff9dc8b5be1106e5b7bfb9c7b2b6 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqForkGateFeedTest.java
|
49c3fd97e7985966531928af173b6f38ac48ff9dc8b5be1106e5b7bfb9c7b2b6 consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqForkGateFeedTest.java
|
||||||
|
2db85c0c1b080123b252b6fb8896266d9d66753cf352826d3ed72c0790973abb consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java
|
||||||
7ea99f24bc1c9131e8ef08f7a2c6f2a08af71ebee011a4cb88a367bd446b6c27 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java
|
7ea99f24bc1c9131e8ef08f7a2c6f2a08af71ebee011a4cb88a367bd446b6c27 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java
|
||||||
5bf41ab0be06328fa1e18bf9c98bfd89b7b16384fe185d96b3385595dabe61b3 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java
|
5bf41ab0be06328fa1e18bf9c98bfd89b7b16384fe185d96b3385595dabe61b3 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java
|
||||||
4c96b06073ff3830f4e0292fc732ec3fb9e0ed50ef741dceedf755b50f18d5ae ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java
|
4c96b06073ff3830f4e0292fc732ec3fb9e0ed50ef741dceedf755b50f18d5ae ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java
|
||||||
|
|||||||
@ -181,6 +181,8 @@ This matters more than the file list, so it is stated before it.
|
|||||||
| Sealing keys read from the v3 epochs, key rotation under an id (AIP-22 stage 3b, `aere.pq.registryV3.keysFrom`, `aere.falcon.keys.next`) | yes (added 2026-09-23) | **no**; proven on testnet 28001 only |
|
| Sealing keys read from the v3 epochs, key rotation under an id (AIP-22 stage 3b, `aere.pq.registryV3.keysFrom`, `aere.falcon.keys.next`) | yes (added 2026-09-23) | **no**; proven on testnet 28001 only |
|
||||||
| Message authorship from VERIFIED post-quantum seals, with the startup guards AERE-PQC-FIN-01/02/03 (AIP-22 stage 4, `aere.pq.finalityBlock`) | yes (added 2026-09-23) | **no**; proven on testnet 28001 only (H_fin 3,117,000) |
|
| Message authorship from VERIFIED post-quantum seals, with the startup guards AERE-PQC-FIN-01/02/03 (AIP-22 stage 4, `aere.pq.finalityBlock`) | yes (added 2026-09-23) | **no**; proven on testnet 28001 only (H_fin 3,117,000) |
|
||||||
| Checkpoint option, a header rule that refuses another hash at a checkpoint height (AIP-22 stage 4, `aere.pq.checkpoint`) | yes (added 2026-09-23) | **no**; not configured anywhere |
|
| Checkpoint option, a header rule that refuses another hash at a checkpoint height (AIP-22 stage 4, `aere.pq.checkpoint`) | yes (added 2026-09-23) | **no**; not configured anywhere |
|
||||||
|
| Headers without ECDSA committed seals, sealed by the proposer's post-quantum seal as extraData element 6 (AIP-22 stage 5, `aere.pq.headerBlock`, guards AERE-PQC-HDR-01/02) | yes (added 2026-09-23) | **no**; proven on testnet 28001 only (H_hdr 3,131,000) |
|
||||||
|
| The hybrid (SLH-DSA) seal of a commit judged, from the v3 key height, with the key of the v3 epoch in force (a rotated row signs with its new key) | yes (corrected 2026-09-23) | **no** (no v3 epoch on chain 2800); on testnet 28001 since 2026-09-23 22:51Z |
|
||||||
|
|
||||||
Everything not yet in force is **disarmed by absence or by height, never by a flag**: the properties that arm them
|
Everything not yet in force is **disarmed by absence or by height, never by a flag**: the properties that arm them
|
||||||
are unset, and unset means never. Each refuses loudly on a value it cannot parse rather than booting
|
are unset, and unset means never. Each refuses loudly on a value it cannot parse rather than booting
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import org.hyperledger.besu.consensus.common.BftValidatorOverrides;
|
|||||||
import org.hyperledger.besu.consensus.common.EpochManager;
|
import org.hyperledger.besu.consensus.common.EpochManager;
|
||||||
import org.hyperledger.besu.consensus.common.ForksSchedule;
|
import org.hyperledger.besu.consensus.common.ForksSchedule;
|
||||||
import org.hyperledger.besu.consensus.common.bft.AerePqFinality;
|
import org.hyperledger.besu.consensus.common.bft.AerePqFinality;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal;
|
||||||
import org.hyperledger.besu.consensus.common.bft.AerePqIdentity;
|
import org.hyperledger.besu.consensus.common.bft.AerePqIdentity;
|
||||||
import org.hyperledger.besu.consensus.common.bft.BftBlockInterface;
|
import org.hyperledger.besu.consensus.common.bft.BftBlockInterface;
|
||||||
import org.hyperledger.besu.consensus.common.bft.BftContext;
|
import org.hyperledger.besu.consensus.common.bft.BftContext;
|
||||||
@ -480,6 +481,15 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
|
|||||||
FalconSealSupport.proposalAttachBlock(),
|
FalconSealSupport.proposalAttachBlock(),
|
||||||
FalconSealSupport.prepareAttachBlock(),
|
FalconSealSupport.prepareAttachBlock(),
|
||||||
FalconSealSupport.roundChangeAttachBlock());
|
FalconSealSupport.roundChangeAttachBlock());
|
||||||
|
// AERE AIP-22 stage 5: H_hdr (aere.pq.headerBlock), from which a header carries the proposer's post-quantum seal
|
||||||
|
// and no ECDSA committed seal; refuses unless H_fin is at or below it (AERE-PQC-HDR-01). Inert when unset.
|
||||||
|
AerePqHeaderSeal.installFromSystemProperties();
|
||||||
|
if (AerePqHeaderSeal.headerBlock() != AerePqIdentity.NEVER) {
|
||||||
|
LOG.info(
|
||||||
|
"AERE PQ HEADER: H_hdr armed at {}; from it a header carries the proposer's post-quantum seal and NO ECDSA"
|
||||||
|
+ " committed seal",
|
||||||
|
AerePqHeaderSeal.headerBlock());
|
||||||
|
}
|
||||||
if (AerePqFinality.finalityBlock() != AerePqIdentity.NEVER) {
|
if (AerePqFinality.finalityBlock() != AerePqIdentity.NEVER) {
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"AERE PQ FINALITY: H_fin armed at {}; from it a message's author is the id of its VERIFIED post-quantum seal and the"
|
"AERE PQ FINALITY: H_fin armed at {}; from it a message's author is the id of its VERIFIED post-quantum seal and the"
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright contributors to Besu / AERE Network.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package org.hyperledger.besu.consensus.common.bft;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AERE AIP-22 stage 5 (plan 9.7): from H_hdr ({@code aere.pq.headerBlock}) a header carries no ECDSA
|
||||||
|
* committed seal; its seventh extraData element is the proposer's Falcon-512 seal, the PROPOSAL layer's
|
||||||
|
* seal over the proposal digest, and a header is admitted between anchors iff that seal verifies for
|
||||||
|
* its {@code coinbase} (an id) at its height. Finality stays with the anchors (stage 4). H_hdr must be
|
||||||
|
* at or above H_fin, because the seal it relies on is only an AUTHOR from H_fin (AERE-PQC-HDR-01).
|
||||||
|
* Unset: never, which is every chain today.
|
||||||
|
*/
|
||||||
|
public final class AerePqHeaderSeal {
|
||||||
|
|
||||||
|
/** The system property naming H_hdr. */
|
||||||
|
public static final String PROPERTY = "aere.pq.headerBlock";
|
||||||
|
|
||||||
|
private static volatile long headerBlock = AerePqIdentity.NEVER;
|
||||||
|
|
||||||
|
private AerePqHeaderSeal() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arms H_hdr.
|
||||||
|
*
|
||||||
|
* @param hHdr H_hdr, or {@link AerePqIdentity#NEVER}
|
||||||
|
*/
|
||||||
|
public static void install(final long hHdr) {
|
||||||
|
if (hHdr != AerePqIdentity.NEVER) {
|
||||||
|
final long hFin = AerePqFinality.finalityBlock();
|
||||||
|
if (hFin == AerePqIdentity.NEVER || hHdr < hFin) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"AERE-PQC-HDR-01: "
|
||||||
|
+ PROPERTY
|
||||||
|
+ "="
|
||||||
|
+ hHdr
|
||||||
|
+ " needs H_fin (aere.pq.finalityBlock) armed at or below it (H_fin "
|
||||||
|
+ (hFin == AerePqIdentity.NEVER ? "unset" : Long.toString(hFin))
|
||||||
|
+ "): the proposer's seal in the header is an author only from H_fin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headerBlock = hHdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disarms (tests). */
|
||||||
|
public static void uninstall() {
|
||||||
|
headerBlock = AerePqIdentity.NEVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* H_hdr as armed.
|
||||||
|
*
|
||||||
|
* @return H_hdr, or {@link AerePqIdentity#NEVER}
|
||||||
|
*/
|
||||||
|
public static long headerBlock() {
|
||||||
|
return headerBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Arms from {@link #PROPERTY}; unset means never. Call after H_fin is installed. */
|
||||||
|
public static void installFromSystemProperties() {
|
||||||
|
final String v = System.getProperty(PROPERTY);
|
||||||
|
if (v == null || v.isBlank()) {
|
||||||
|
uninstall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final long h;
|
||||||
|
try {
|
||||||
|
h = Long.parseLong(v.trim());
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
throw new IllegalStateException("AERE-PQC-HDR-01: " + PROPERTY + "='" + v + "' is not a height", e);
|
||||||
|
}
|
||||||
|
install(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a header at this height carries the proposer's seal instead of ECDSA committed seals.
|
||||||
|
*
|
||||||
|
* @param height the header's height
|
||||||
|
* @return true from H_hdr
|
||||||
|
*/
|
||||||
|
public static boolean active(final long height) {
|
||||||
|
return height >= headerBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -117,7 +117,10 @@ public class BftBlockInterface implements BlockInterface {
|
|||||||
// AERE ANCHOR V2 (2026-09-03, D-328): the scheme-tagged certificate rides along too. Without
|
// AERE ANCHOR V2 (2026-09-03, D-328): the scheme-tagged certificate rides along too. Without
|
||||||
// this line every v2 header lost its certificate at the round substitution and the fleet
|
// this line every v2 header lost its certificate at the round substitution and the fleet
|
||||||
// refused its own first v2 anchor (testnet 28001, block 168032, 3 hours stalled).
|
// refused its own first v2 anchor (testnet 28001, block 168032, 3 hours stalled).
|
||||||
prevExtraData.getHybridSeals());
|
prevExtraData.getHybridSeals(),
|
||||||
|
// AERE AIP-22 stage 5: and the proposer's seal (element 6), for the same reason; it sits outside
|
||||||
|
// both hash pre-images, so carrying it changes no hash
|
||||||
|
prevExtraData.getPqProposerSeal());
|
||||||
|
|
||||||
final BlockHeaderBuilder headerBuilder = BlockHeaderBuilder.fromHeader(block.getHeader());
|
final BlockHeaderBuilder headerBuilder = BlockHeaderBuilder.fromHeader(block.getHeader());
|
||||||
headerBuilder
|
headerBuilder
|
||||||
|
|||||||
@ -49,6 +49,12 @@ public class BftExtraData implements ParsedExtraData {
|
|||||||
private final Collection<FalconSeal> falconSeals;
|
private final Collection<FalconSeal> falconSeals;
|
||||||
/** AERE ANCHOR V2: the scheme-tagged certificate; empty on every header below the v2 height. */
|
/** AERE ANCHOR V2: the scheme-tagged certificate; empty on every header below the v2 height. */
|
||||||
private final List<SchemeSeal> hybridSeals;
|
private final List<SchemeSeal> hybridSeals;
|
||||||
|
/**
|
||||||
|
* AERE AIP-22 stage 5: the proposer's post-quantum seal (element 6 of the stored extraData, from
|
||||||
|
* H_hdr), the PROPOSAL layer's seal over the proposal digest; outside every hash, like the
|
||||||
|
* certificate. Empty on every header below H_hdr.
|
||||||
|
*/
|
||||||
|
private final Optional<FalconSeal> proposerSeal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Bft extra data (no Falcon seals). Retained for callers, notably the IBFT
|
* Instantiates a new Bft extra data (no Falcon seals). Retained for callers, notably the IBFT
|
||||||
@ -103,7 +109,33 @@ public class BftExtraData implements ParsedExtraData {
|
|||||||
final Collection<Address> validators,
|
final Collection<Address> validators,
|
||||||
final Collection<FalconSeal> falconSeals,
|
final Collection<FalconSeal> falconSeals,
|
||||||
final List<SchemeSeal> hybridSeals) {
|
final List<SchemeSeal> hybridSeals) {
|
||||||
|
this(vanityData, seals, vote, round, validators, falconSeals, hybridSeals, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full constructor with the proposer's post-quantum seal (AIP-22 stage 5).
|
||||||
|
*
|
||||||
|
* @param vanityData the vanity data
|
||||||
|
* @param seals the ECDSA committed seals (from H_hdr: empty)
|
||||||
|
* @param vote the vote
|
||||||
|
* @param round the round
|
||||||
|
* @param validators the validators
|
||||||
|
* @param falconSeals the v1 Falcon certificate, or empty
|
||||||
|
* @param hybridSeals the v2 certificate, or empty
|
||||||
|
* @param proposerSeal the proposer's seal (element 6), or empty
|
||||||
|
*/
|
||||||
|
public BftExtraData(
|
||||||
|
final Bytes vanityData,
|
||||||
|
final Collection<SECPSignature> seals,
|
||||||
|
final Optional<Vote> vote,
|
||||||
|
final int round,
|
||||||
|
final Collection<Address> validators,
|
||||||
|
final Collection<FalconSeal> falconSeals,
|
||||||
|
final List<SchemeSeal> hybridSeals,
|
||||||
|
final Optional<FalconSeal> proposerSeal) {
|
||||||
checkNotNull(vanityData);
|
checkNotNull(vanityData);
|
||||||
|
checkNotNull(proposerSeal);
|
||||||
|
this.proposerSeal = proposerSeal;
|
||||||
checkNotNull(hybridSeals);
|
checkNotNull(hybridSeals);
|
||||||
if (!hybridSeals.isEmpty() && falconSeals != null && !falconSeals.isEmpty()) {
|
if (!hybridSeals.isEmpty() && falconSeals != null && !falconSeals.isEmpty()) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@ -184,6 +216,30 @@ public class BftExtraData implements ParsedExtraData {
|
|||||||
return hybridSeals;
|
return hybridSeals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The proposer's post-quantum seal (AIP-22 stage 5, element 6).
|
||||||
|
*
|
||||||
|
* @return the seal, or empty below H_hdr
|
||||||
|
*/
|
||||||
|
public Optional<FalconSeal> getPqProposerSeal() {
|
||||||
|
return proposerSeal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The same extra data with another proposer seal and ECDSA seals, everything else carried over
|
||||||
|
* (the certificate included): the one way to seal a stage-5 header, so no caller can drop the
|
||||||
|
* certificate while adding the seal (the lesson of 2026-09-03, D-328).
|
||||||
|
*
|
||||||
|
* @param newSeals the ECDSA committed seals (from H_hdr: empty)
|
||||||
|
* @param newProposerSeal the proposer's seal
|
||||||
|
* @return the new extra data
|
||||||
|
*/
|
||||||
|
public BftExtraData withSeals(
|
||||||
|
final Collection<SECPSignature> newSeals, final Optional<FalconSeal> newProposerSeal) {
|
||||||
|
return new BftExtraData(
|
||||||
|
vanityData, newSeals, vote, round, validators, falconSeals, hybridSeals, newProposerSeal);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "BftExtraData{"
|
return "BftExtraData{"
|
||||||
@ -201,6 +257,8 @@ public class BftExtraData implements ParsedExtraData {
|
|||||||
+ falconSeals
|
+ falconSeals
|
||||||
+ ", hybridSeals="
|
+ ", hybridSeals="
|
||||||
+ hybridSeals
|
+ hybridSeals
|
||||||
|
+ ", proposerSeal="
|
||||||
|
+ proposerSeal
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ package org.hyperledger.besu.consensus.qbft.core.statemachine;
|
|||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal;
|
||||||
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
|
import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier;
|
||||||
import org.hyperledger.besu.consensus.common.bft.FalconSeal;
|
import org.hyperledger.besu.consensus.common.bft.FalconSeal;
|
||||||
import org.hyperledger.besu.consensus.common.bft.FalconSealSupport;
|
import org.hyperledger.besu.consensus.common.bft.FalconSealSupport;
|
||||||
@ -539,6 +540,27 @@ public class QbftRound {
|
|||||||
// least K valid seals, K being the configured schedule (6 of 9 on chain 2800 today). Between
|
// least K valid seals, K being the configured schedule (6 of 9 on chain 2800 today). Between
|
||||||
// anchor heights nothing is written. The older wording here said "a >= 2f+1 Falcon quorum
|
// anchor heights nothing is written. The older wording here said "a >= 2f+1 Falcon quorum
|
||||||
// certificate" and described the legacy per-block rule, which is retired at the anchor block.
|
// certificate" and described the legacy per-block rule, which is retired at the anchor block.
|
||||||
|
// AERE AIP-22 stage 5: from H_hdr the sealed header carries no ECDSA committed seal and the
|
||||||
|
// accepted proposal's post-quantum seal as element 6 (the same bytes on every node). A proposal
|
||||||
|
// without one cannot have been accepted from H_fin <= H_hdr; if it happens anyway nothing is
|
||||||
|
// imported, because the only other header this node could build is one every node refuses.
|
||||||
|
final long aereHeight = roundState.getRoundIdentifier().getSequenceNumber();
|
||||||
|
if (AerePqHeaderSeal.active(aereHeight)) {
|
||||||
|
final Optional<FalconSeal> proposerSeal = roundState.getProposalFalconSeal();
|
||||||
|
if (proposerSeal.isEmpty()) {
|
||||||
|
LOG.error(
|
||||||
|
"AERE-PQC-HDR-02: committed at {} but the accepted proposal carries no post-quantum seal;"
|
||||||
|
+ " a header from H_hdr cannot be sealed without it. Not importing.",
|
||||||
|
aereHeight);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
importSealedBlock(
|
||||||
|
blockCreator.createSealedBlockWithProposerSeal(
|
||||||
|
roundState.getProposedBlock().get(),
|
||||||
|
roundState.getRoundIdentifier().getRoundNumber(),
|
||||||
|
proposerSeal.get()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Collection<FalconSeal> falconSeals = roundState.getFalconSeals();
|
final Collection<FalconSeal> falconSeals = roundState.getFalconSeals();
|
||||||
final QbftBlock blockToImport =
|
final QbftBlock blockToImport =
|
||||||
falconSeals.isEmpty()
|
falconSeals.isEmpty()
|
||||||
@ -552,6 +574,11 @@ public class QbftRound {
|
|||||||
roundState.getCommitSeals(),
|
roundState.getCommitSeals(),
|
||||||
falconSeals);
|
falconSeals);
|
||||||
|
|
||||||
|
importSealedBlock(blockToImport);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Imports a sealed block into the chain (shared by the stage-5 and the ECDSA sealing paths). */
|
||||||
|
private void importSealedBlock(final QbftBlock blockToImport) {
|
||||||
final long blockNumber = blockToImport.getHeader().getNumber();
|
final long blockNumber = blockToImport.getHeader().getNumber();
|
||||||
if (getRoundIdentifier().getRoundNumber() > 0) {
|
if (getRoundIdentifier().getRoundNumber() > 0) {
|
||||||
LOG.info(
|
LOG.info(
|
||||||
|
|||||||
@ -166,6 +166,16 @@ public class RoundState {
|
|||||||
return proposalMessage.map(p -> p.getSignedPayload().getPayload().getProposedBlock());
|
return proposalMessage.map(p -> p.getSignedPayload().getPayload().getProposedBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIP-22 stage 5: the post-quantum seal of the accepted proposal, which becomes element 6 of the
|
||||||
|
* sealed header from H_hdr (every node writes the same one, the proposer's).
|
||||||
|
*
|
||||||
|
* @return the proposal's seal, or empty
|
||||||
|
*/
|
||||||
|
public Optional<FalconSeal> getProposalFalconSeal() {
|
||||||
|
return proposalMessage.flatMap(p -> p.getSignedPayload().getPayload().getFalconSeal());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets proposed block access list.
|
* Gets proposed block access list.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -79,6 +79,22 @@ public interface QbftBlockCreator {
|
|||||||
return createSealedBlock(block, roundNumber, commitSeals);
|
return createSealedBlock(block, roundNumber, commitSeals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIP-22 stage 5: seal a block from H_hdr, with NO ECDSA committed seal and the proposer's
|
||||||
|
* post-quantum seal as element 6, everything else of the proposed header carried over (the anchor
|
||||||
|
* certificate included). An implementation that cannot do this must say so, never seal the old way.
|
||||||
|
*
|
||||||
|
* @param block the proposed block
|
||||||
|
* @param roundNumber the round number
|
||||||
|
* @param proposerSeal the proposal's post-quantum seal
|
||||||
|
* @return the sealed block
|
||||||
|
*/
|
||||||
|
default QbftBlock createSealedBlockWithProposerSeal(
|
||||||
|
final QbftBlock block, final int roundNumber, final FalconSeal proposerSeal) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"AERE-PQC-HDR-02: this block creator cannot seal a stage-5 header (proposer seal, no ECDSA seals)");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience empty Falcon seal collection.
|
* Convenience empty Falcon seal collection.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -312,15 +312,28 @@ public final class PqCommitEnforcement {
|
|||||||
+ " but the Falcon seal on the same commit is index "
|
+ " but the Falcon seal on the same commit is index "
|
||||||
+ falconSeal.getValidatorIndex() + " - one commit, one signer");
|
+ falconSeal.getValidatorIndex() + " - one commit, one signer");
|
||||||
}
|
}
|
||||||
final Optional<byte[]> bound = hybridRegistry.address(found.getValidatorIndex());
|
// AIP-22 stage 3b: from H_k the keys and the index->author binding are those of the v3 epoch in force, as in the anchor
|
||||||
if (bound.isEmpty()
|
// producer and PqAnchorSealsRule. Paid 2026-09-23 on testnet 28001: this check alone still read the static v2 hybrid
|
||||||
|| !AerePqIdentity.author(Address.wrap(Bytes.wrap(bound.get())), height).equals(author)) {
|
// registry, so after the rotation at 3,097,600 the ROTATED row (index 2) signed with its new SLH-DSA key and every one of its
|
||||||
|
// commits at an anchor's parent was refused here; latent while four other validators sufficed, it stopped the chain at
|
||||||
|
// 3,135,679 when the Nethermind validator fell behind.
|
||||||
|
final boolean v3 = AerePqIdentity.keysFromV3(height);
|
||||||
|
final Optional<Address> boundId =
|
||||||
|
v3
|
||||||
|
? AerePqIdentity.v3IdAt(height, found.getValidatorIndex())
|
||||||
|
: hybridRegistry
|
||||||
|
.address(found.getValidatorIndex())
|
||||||
|
.map(a -> AerePqIdentity.author(Address.wrap(Bytes.wrap(a)), height));
|
||||||
|
if (boundId.isEmpty() || !boundId.get().equals(author)) {
|
||||||
return Optional.of(
|
return Optional.of(
|
||||||
"AERE HIBRID: index " + found.getValidatorIndex()
|
"AERE HIBRID: index " + found.getValidatorIndex()
|
||||||
+ " is not bound to the commit author " + author + " in the hybrid registry");
|
+ " is not bound to the commit author " + author
|
||||||
|
+ (v3 ? " in the v3 epoch in force" : " in the hybrid registry"));
|
||||||
}
|
}
|
||||||
final Optional<byte[]> publicKey =
|
final Optional<byte[]> publicKey =
|
||||||
hybridRegistry.publicKey(found.getValidatorIndex(), schemeId);
|
v3
|
||||||
|
? AerePqIdentity.v3KeyAt(height, found.getValidatorIndex(), schemeId)
|
||||||
|
: hybridRegistry.publicKey(found.getValidatorIndex(), schemeId);
|
||||||
if (publicKey.isEmpty()) {
|
if (publicKey.isEmpty()) {
|
||||||
return Optional.of(
|
return Optional.of(
|
||||||
"AERE HIBRID: the hybrid registry holds no " + schemeId + " key for index "
|
"AERE HIBRID: the hybrid registry holds no " + schemeId + " key for index "
|
||||||
|
|||||||
@ -0,0 +1,171 @@
|
|||||||
|
/* AIP-22 stage 3b: the hybrid (SLH-DSA) seal of a COMMIT is judged, from H_k, with the key of the v3 epoch in force, not with the
|
||||||
|
* static v2 hybrid registry.
|
||||||
|
*
|
||||||
|
* Paid 2026-09-23 on testnet 28001: after the rotation at 3,097,600 the rotated row (index 2) signed its commits with its NEW
|
||||||
|
* SLH-DSA key, as the v3 epoch says, and PqCommitEnforcement still read the OLD key from the v2 hybrid registry, so every one of its
|
||||||
|
* commits at an anchor's parent was refused for six hours, invisibly while four other validators sufficed, until the chain stopped at
|
||||||
|
* 3,135,679 when the Nethermind validator fell behind. The anchor producer and PqAnchorSealsRule were already on v3.
|
||||||
|
*
|
||||||
|
* Real cryptography (Falcon-512 and SLH-DSA-SHA2-128s generated on every run, TEST keys); a v3 epoch built from the canonical row
|
||||||
|
* bytes; the v2 registry built from properties exactly as in production. The negative control plants, at run time, "keys never from
|
||||||
|
* v3" in PqCommitEnforcement: the two cases from H_k must turn red. */
|
||||||
|
package org.hyperledger.besu.consensus.qbft.core.validation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqIdentity;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.FalconSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.HybridSignerRegistry;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqRegistryV3;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqSchemeSchedule;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqSignerRegistry;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.SchemeSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.SealScheme;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.SealSchemes;
|
||||||
|
import org.hyperledger.besu.crypto.SECPSignature;
|
||||||
|
import org.hyperledger.besu.crypto.SecureRandomProvider;
|
||||||
|
import org.hyperledger.besu.cryptoservices.NodeKey;
|
||||||
|
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
|
||||||
|
import org.hyperledger.besu.datatypes.Address;
|
||||||
|
import org.hyperledger.besu.datatypes.Hash;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes;
|
||||||
|
import org.apache.tuweni.bytes.Bytes32;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class PqHybridRotationV3Test {
|
||||||
|
private static final long CHAIN = 28001L;
|
||||||
|
private static final long H_ARMARE = 1_000L;
|
||||||
|
private static final long H_HIBRID = 2_000L;
|
||||||
|
private static final long H_K = 3_000L;
|
||||||
|
private static final Hash DIGEST = Hash.hash(Bytes.of(7, 7));
|
||||||
|
private static final SealScheme FALCON = SealSchemes.FALCON_512;
|
||||||
|
private static final SealScheme SLH = SealSchemes.SLH_DSA_128S;
|
||||||
|
|
||||||
|
private final SecureRandom random = SecureRandomProvider.createSecureRandom();
|
||||||
|
private SealScheme.GeneratedPair falcon0;
|
||||||
|
private SealScheme.GeneratedPair slhOld;
|
||||||
|
private SealScheme.GeneratedPair slhNew;
|
||||||
|
private Address addr0;
|
||||||
|
private Address id0;
|
||||||
|
private PqCommitEnforcement enforcement;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
falcon0 = FALCON.generate(random);
|
||||||
|
slhOld = SLH.generate(random);
|
||||||
|
slhNew = SLH.generate(random);
|
||||||
|
final NodeKey nk = NodeKeyUtils.generate();
|
||||||
|
addr0 = org.hyperledger.besu.ethereum.core.Util.publicKeyToAddress(nk.getPublicKey());
|
||||||
|
|
||||||
|
// the v2 hybrid registry: index 0 is addr0, with the OLD SLH-DSA key (what the static registry keeps after a v3 rotation)
|
||||||
|
final Properties p = new Properties();
|
||||||
|
p.setProperty("formatVersion", HybridSignerRegistry.FORMAT_VERSION);
|
||||||
|
p.setProperty("chainId", String.valueOf(CHAIN));
|
||||||
|
p.setProperty("count", "1");
|
||||||
|
p.setProperty("0.addr", addr0.toHexString());
|
||||||
|
p.setProperty("0.key." + FALCON.id(), Bytes.wrap(falcon0.publicRegistryForm()).toHexString());
|
||||||
|
p.setProperty("0.key." + SLH.id(), Bytes.wrap(slhOld.publicRegistryForm()).toHexString());
|
||||||
|
final HybridSignerRegistry v2 = HybridSignerRegistry.fromProperties(p, "proba");
|
||||||
|
|
||||||
|
// the v3 epoch at H_k: row 0 keeps its Falcon key and carries the NEW SLH-DSA key (a rotation of the hybrid key)
|
||||||
|
final byte[] std = new byte[897];
|
||||||
|
std[0] = 0x09;
|
||||||
|
System.arraycopy(falcon0.publicRegistryForm(), 0, std, 1, 896);
|
||||||
|
final byte[] id = PqRegistryV3.idOf(std);
|
||||||
|
id0 = Address.wrap(Bytes.wrap(id));
|
||||||
|
final byte[] slhPub = slhNew.publicRegistryForm();
|
||||||
|
final byte[] popF = FALCON.sign(falcon0.privateKey(),
|
||||||
|
PqRegistryV3.popMessage(CHAIN, H_K, 1, 0, id, FALCON.wireId(), std).toArrayUnsafe()).orElseThrow();
|
||||||
|
final byte[] popS = SLH.sign(slhNew.privateKey(),
|
||||||
|
PqRegistryV3.popMessage(CHAIN, H_K, 1, 0, id, SLH.wireId(), slhPub).toArrayUnsafe()).orElseThrow();
|
||||||
|
final TreeMap<Integer, PqRegistryV3.Key> keys = new TreeMap<>();
|
||||||
|
keys.put(FALCON.wireId() & 0xff, new PqRegistryV3.Key(FALCON, std, popF));
|
||||||
|
keys.put(SLH.wireId() & 0xff, new PqRegistryV3.Key(SLH, slhPub, popS));
|
||||||
|
final PqRegistryV3.Row bare = new PqRegistryV3.Row(0, id, keys, null, Map.of(), null, null);
|
||||||
|
final SECPSignature claim = nk.sign(PqRegistryV3.claimDigest(CHAIN, PqRegistryV3.rowHash(CHAIN, H_K, 1, bare)));
|
||||||
|
final PqRegistryV3.Row row =
|
||||||
|
new PqRegistryV3.Row(0, id, keys, null, Map.of(), addr0.getBytes().toArrayUnsafe(), claim.encodedBytes().toArrayUnsafe());
|
||||||
|
final Bytes32 rh = PqRegistryV3.rowHash(CHAIN, H_K, 1, row);
|
||||||
|
final Bytes32 root = PqRegistryV3.merkleRoot(List.of(rh));
|
||||||
|
final TreeMap<Long, PqRegistryV3.Registry> epochs = new TreeMap<>();
|
||||||
|
epochs.put(H_K, new PqRegistryV3.Registry(CHAIN, H_K, 1, List.of(row), List.of(rh), root,
|
||||||
|
PqRegistryV3.registryHash(CHAIN, H_K, 1, root)));
|
||||||
|
AerePqIdentity.uninstall();
|
||||||
|
AerePqIdentity.install(new AerePqIdentity.Config(H_K, epochs, H_K));
|
||||||
|
|
||||||
|
// the Falcon part of the commit check: index 0 is addr0 below H_k and its id from H_k, the Falcon key unchanged
|
||||||
|
final PqSignerRegistry falconReg =
|
||||||
|
new PqSignerRegistry() {
|
||||||
|
@Override
|
||||||
|
public Address addressForIndexAtHistoric(final long h, final int i) {
|
||||||
|
return addressForIndexAtOwnHead(h, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address addressForIndexAtOwnHead(final long h, final int i) {
|
||||||
|
return i != 0 ? null : (h >= H_K ? id0 : addr0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyAtHistoric(final long h, final int i, final Bytes m, final Bytes s) {
|
||||||
|
return verifyAtOwnHead(h, i, m, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyAtOwnHead(final long h, final int i, final Bytes m, final Bytes s) {
|
||||||
|
return i == 0 && FALCON.verifyRaw(falcon0.publicRegistryForm(), m.toArray(), s.toArray());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final PqSchemeSchedule orar =
|
||||||
|
PqSchemeSchedule.parse(H_ARMARE + ":" + FALCON.id() + "," + H_HIBRID + ":" + FALCON.id() + "+" + SLH.id());
|
||||||
|
enforcement = new PqCommitEnforcement(H_ARMARE, falconReg, orar, v2, h -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void disarm() {
|
||||||
|
AerePqIdentity.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<FalconSeal> falconSeal() {
|
||||||
|
return Optional.of(new FalconSeal(0, Bytes.wrap(FALCON.sign(falcon0.privateKey(), DIGEST.getBytes().toArray()).orElseThrow())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SchemeSeal> slhSeal(final SealScheme.GeneratedPair key) {
|
||||||
|
return List.of(new SchemeSeal(SLH.wireId(), 0, Bytes.wrap(SLH.sign(key.privateKey(), DIGEST.getBytes().toArray()).orElseThrow())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromHkTheRotatedSlhKeyOfTheV3EpochCounts() {
|
||||||
|
assertThat(enforcement.refusal(H_K + 5, id0, DIGEST, falconSeal(), slhSeal(slhNew))).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromHkTheOldSlhKeyOfTheStaticRegistryNoLongerCounts() {
|
||||||
|
final Optional<String> r = enforcement.refusal(H_K + 5, id0, DIGEST, falconSeal(), slhSeal(slhOld));
|
||||||
|
assertThat(r).isPresent();
|
||||||
|
assertThat(r.get()).contains("does NOT verify").contains(SLH.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void belowHkTheStaticRegistryDecidesAsBefore() {
|
||||||
|
assertThat(enforcement.refusal(H_K - 5, addr0, DIGEST, falconSeal(), slhSeal(slhOld))).isEmpty();
|
||||||
|
assertThat(enforcement.refusal(H_K - 5, addr0, DIGEST, falconSeal(), slhSeal(slhNew))).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromHkTheHybridSealCannotVouchForAnotherAuthor() {
|
||||||
|
final Optional<String> r =
|
||||||
|
enforcement.refusal(H_K + 5, Address.fromHexString("0x" + "cd".repeat(20)), DIGEST, falconSeal(), slhSeal(slhNew));
|
||||||
|
assertThat(r).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,13 +27,13 @@ import org.hyperledger.besu.consensus.common.bft.BftHelpers;
|
|||||||
import org.hyperledger.besu.consensus.common.bft.FalconSealSupport;
|
import org.hyperledger.besu.consensus.common.bft.FalconSealSupport;
|
||||||
import org.hyperledger.besu.consensus.common.bft.PqAnchorConfig;
|
import org.hyperledger.besu.consensus.common.bft.PqAnchorConfig;
|
||||||
import org.hyperledger.besu.consensus.common.bft.headervalidationrules.BftCoinbaseValidationRule;
|
import org.hyperledger.besu.consensus.common.bft.headervalidationrules.BftCoinbaseValidationRule;
|
||||||
import org.hyperledger.besu.consensus.common.bft.headervalidationrules.BftCommitSealsValidationRule;
|
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.AereBaseFeeImportRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.AereBaseFeeImportRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.FalconSealValidationRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.FalconSealValidationRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestAttachedRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestAttachedRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorSealsRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorSealsRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqCheckpointRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqCheckpointRule;
|
||||||
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqHeaderSealRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqEmergencyShoutRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqEmergencyShoutRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqRegistryBindingRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqRegistryBindingRule;
|
||||||
import org.hyperledger.besu.consensus.qbft.headervalidationrules.QbftValidatorsValidationRule;
|
import org.hyperledger.besu.consensus.qbft.headervalidationrules.QbftValidatorsValidationRule;
|
||||||
@ -162,7 +162,9 @@ public class QbftBlockHeaderValidationRulesetFactory {
|
|||||||
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
|
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
|
||||||
.addRule(new QbftValidatorsValidationRule(useValidatorContract))
|
.addRule(new QbftValidatorsValidationRule(useValidatorContract))
|
||||||
.addRule(new BftCoinbaseValidationRule())
|
.addRule(new BftCoinbaseValidationRule())
|
||||||
.addRule(new BftCommitSealsValidationRule())
|
// AERE AIP-22 stage 5: upstream's committed seals rule below H_hdr, unchanged; from H_hdr the proposer's
|
||||||
|
// post-quantum seal (element 6) and an EMPTY committed-seal element (PqHeaderSealRule)
|
||||||
|
.addRule(PqHeaderSealRule.forFleet(pqAnchorConfig.chainId()))
|
||||||
// AERE ANCORA-V2: the legacy log-only Falcon rule now stands down at H. With an
|
// AERE ANCORA-V2: the legacy log-only Falcon rule now stands down at H. With an
|
||||||
// unconfigured anchor this is Long.MAX_VALUE, i.e. it never stands down and behaviour is
|
// unconfigured anchor this is Long.MAX_VALUE, i.e. it never stands down and behaviour is
|
||||||
// unchanged.
|
// unchanged.
|
||||||
|
|||||||
@ -164,6 +164,20 @@ public class QbftExtraDataCodec extends BftExtraDataCodec {
|
|||||||
hybridSeals = Collections.emptyList();
|
hybridSeals = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AERE AIP-22 stage 5: an OPTIONAL seventh element, the proposer's seal [index, signature], after
|
||||||
|
// the certificate slot. When it is present the certificate slot is ALWAYS written (an empty list
|
||||||
|
// when the header carries no certificate), so the POSITION tells the two apart, never the shape
|
||||||
|
// (a proposer seal from index 2 would otherwise look like the head of a v2 certificate), and the
|
||||||
|
// canonical round trip below keeps every value to exactly one encoding.
|
||||||
|
Optional<FalconSeal> proposerSeal = Optional.empty();
|
||||||
|
if (!rlpInput.isEndOfCurrentList()) {
|
||||||
|
rlpInput.enterList();
|
||||||
|
final int idx = rlpInput.readIntScalar();
|
||||||
|
final Bytes sig = rlpInput.readBytes();
|
||||||
|
rlpInput.leaveList();
|
||||||
|
proposerSeal = Optional.of(new FalconSeal(idx, sig));
|
||||||
|
}
|
||||||
|
|
||||||
// AERE FIX-MALEABILITATE-BLOC (2026-08-01), gate 1 of 2: the leave is STRICT.
|
// AERE FIX-MALEABILITATE-BLOC (2026-08-01), gate 1 of 2: the leave is STRICT.
|
||||||
//
|
//
|
||||||
// Upstream ends this decode with leaveListLenient(), which silently discards anything still
|
// Upstream ends this decode with leaveListLenient(), which silently discards anything still
|
||||||
@ -177,7 +191,8 @@ public class QbftExtraDataCodec extends BftExtraDataCodec {
|
|||||||
rlpInput.leaveList();
|
rlpInput.leaveList();
|
||||||
|
|
||||||
final BftExtraData decoded =
|
final BftExtraData decoded =
|
||||||
new BftExtraData(vanityData, seals, vote, round, validators, falconSeals, hybridSeals);
|
new BftExtraData(
|
||||||
|
vanityData, seals, vote, round, validators, falconSeals, hybridSeals, proposerSeal);
|
||||||
|
|
||||||
// AERE FIX-MALEABILITATE-BLOC (2026-08-01), gate 2 of 2: CANONICAL ROUND TRIP.
|
// AERE FIX-MALEABILITATE-BLOC (2026-08-01), gate 2 of 2: CANONICAL ROUND TRIP.
|
||||||
//
|
//
|
||||||
@ -243,6 +258,16 @@ public class QbftExtraDataCodec extends BftExtraDataCodec {
|
|||||||
rlp.writeBytes(falconSeal.getSignature());
|
rlp.writeBytes(falconSeal.getSignature());
|
||||||
rlp.endList();
|
rlp.endList();
|
||||||
});
|
});
|
||||||
|
} else if (bftExtraData.getPqProposerSeal().isPresent()) {
|
||||||
|
// AERE AIP-22 stage 5: the certificate slot stays in place, empty, so the seal is element 6
|
||||||
|
encoder.writeEmptyList();
|
||||||
|
}
|
||||||
|
if (bftExtraData.getPqProposerSeal().isPresent()) {
|
||||||
|
final FalconSeal ps = bftExtraData.getPqProposerSeal().get();
|
||||||
|
encoder.startList();
|
||||||
|
encoder.writeIntScalar(ps.getValidatorIndex());
|
||||||
|
encoder.writeBytes(ps.getSignature());
|
||||||
|
encoder.endList();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
encoder.writeEmptyList();
|
encoder.writeEmptyList();
|
||||||
|
|||||||
@ -87,6 +87,38 @@ public class QbftBlockCreatorAdaptor implements QbftBlockCreator {
|
|||||||
return createSealedBlock(block, roundNumber, commitSeals, Collections.emptyList());
|
return createSealedBlock(block, roundNumber, commitSeals, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIP-22 stage 5: the stage-5 header, from H_hdr. The proposed header's extraData is carried over
|
||||||
|
* whole (vanity with the anchor digest, validators, vote, the anchor certificate verbatim) through
|
||||||
|
* {@link BftExtraData#withSeals}, so nothing the proposer chose can be dropped here; only the round
|
||||||
|
* is set to the commit round, the ECDSA committed seals are EMPTY and element 6 is the proposal's
|
||||||
|
* post-quantum seal. Every node seals the same bytes: the header is no longer assembled from what
|
||||||
|
* each node heard.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public QbftBlock createSealedBlockWithProposerSeal(
|
||||||
|
final QbftBlock block, final int roundNumber, final FalconSeal proposerSeal) {
|
||||||
|
final Block besuBlock = AdaptorUtil.toBesuBlock(block);
|
||||||
|
final BftExtraData initial = bftExtraDataCodec.decode(AdaptorUtil.toBesuBlockHeader(block.getHeader()));
|
||||||
|
final BftExtraData withRound =
|
||||||
|
new BftExtraData(
|
||||||
|
initial.getVanityData(),
|
||||||
|
Collections.emptyList(),
|
||||||
|
initial.getVote(),
|
||||||
|
roundNumber,
|
||||||
|
initial.getValidators(),
|
||||||
|
initial.getFalconSeals(),
|
||||||
|
initial.getHybridSeals(),
|
||||||
|
initial.getPqProposerSeal());
|
||||||
|
final BftExtraData sealed = withRound.withSeals(Collections.emptyList(), Optional.of(proposerSeal));
|
||||||
|
final BlockHeader header =
|
||||||
|
BlockHeaderBuilder.fromHeader(AdaptorUtil.toBesuBlockHeader(block.getHeader()))
|
||||||
|
.extraData(bftExtraDataCodec.encode(sealed))
|
||||||
|
.blockHeaderFunctions(BftBlockHeaderFunctions.forOnchainBlock(bftExtraDataCodec))
|
||||||
|
.buildBlockHeader();
|
||||||
|
return new QbftBlockAdaptor(new Block(header, besuBlock.getBody()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QbftBlock createSealedBlock(
|
public QbftBlock createSealedBlock(
|
||||||
final QbftBlock block,
|
final QbftBlock block,
|
||||||
|
|||||||
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright contributors to Besu / AERE Network.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package org.hyperledger.besu.consensus.qbft.headervalidationrules;
|
||||||
|
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqFinality;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqIdentity;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.BftBlockHashing;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.FalconSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqAnchor;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqSignerRegistry;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.headervalidationrules.BftCommitSealsValidationRule;
|
||||||
|
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec;
|
||||||
|
import org.hyperledger.besu.datatypes.Address;
|
||||||
|
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||||
|
import org.hyperledger.besu.ethereum.mainnet.AttachedBlockHeaderValidationRule;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes32;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIP-22 stage 5: the seal rule of a header. Below H_hdr it IS the upstream rule (a quorum of ECDSA
|
||||||
|
* committed seals of the parent's validators), unchanged. From H_hdr a header is admitted iff its ECDSA
|
||||||
|
* committed-seal element is EMPTY, its seventh element carries the proposer's Falcon-512 seal, the seal's
|
||||||
|
* index names the header's {@code coinbase} (an id) in the v3 epoch in force at its height, and the seal
|
||||||
|
* verifies over the PROPOSAL message recomputed from the stored header (chain id, height, the round in
|
||||||
|
* element 3, the proposal digest, i.e. the committed-seal hash of the header). Finality stays with the
|
||||||
|
* anchors (AIP-22 section 4); this rule is what admits a header between them.
|
||||||
|
*/
|
||||||
|
public class PqHeaderSealRule implements AttachedBlockHeaderValidationRule {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PqHeaderSealRule.class);
|
||||||
|
|
||||||
|
private final AttachedBlockHeaderValidationRule belowHdr;
|
||||||
|
private final BftExtraDataCodec codec;
|
||||||
|
private final long chainId;
|
||||||
|
private final AerePqFinality.SealVerifier verifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rule.
|
||||||
|
*
|
||||||
|
* @param belowHdr the rule below H_hdr (upstream's committed seals rule in production)
|
||||||
|
* @param codec the extraData codec
|
||||||
|
* @param chainId the chain id every seal message carries
|
||||||
|
* @param verifier the seal verifier (the anchor rules' historic door in production)
|
||||||
|
*/
|
||||||
|
public PqHeaderSealRule(
|
||||||
|
final AttachedBlockHeaderValidationRule belowHdr,
|
||||||
|
final BftExtraDataCodec codec,
|
||||||
|
final long chainId,
|
||||||
|
final AerePqFinality.SealVerifier verifier) {
|
||||||
|
this.belowHdr = belowHdr;
|
||||||
|
this.codec = codec;
|
||||||
|
this.chainId = chainId;
|
||||||
|
this.verifier = verifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fleet's rule: upstream's committed seals rule below H_hdr, the verifier of the header rules above.
|
||||||
|
*
|
||||||
|
* @param chainId the chain id
|
||||||
|
* @return the rule
|
||||||
|
*/
|
||||||
|
public static PqHeaderSealRule forFleet(final long chainId) {
|
||||||
|
final PqSignerRegistry registry = PqSignerRegistry.falconSealSupport();
|
||||||
|
return new PqHeaderSealRule(
|
||||||
|
new BftCommitSealsValidationRule(), new QbftExtraDataCodec(), chainId, registry::verifyAtHistoric);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate(
|
||||||
|
final BlockHeader header, final BlockHeader parent, final ProtocolContext protocolContext) {
|
||||||
|
if (!AerePqHeaderSeal.active(header.getNumber())) {
|
||||||
|
return belowHdr.validate(header, parent, protocolContext);
|
||||||
|
}
|
||||||
|
final Optional<String> r = refusal(header);
|
||||||
|
r.ifPresent(LOG::info);
|
||||||
|
return r.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Why a header at or above H_hdr is refused, if it is.
|
||||||
|
*
|
||||||
|
* @param header the header
|
||||||
|
* @return the refusal, or empty
|
||||||
|
*/
|
||||||
|
public Optional<String> refusal(final BlockHeader header) {
|
||||||
|
final long h = header.getNumber();
|
||||||
|
final BftExtraData ed;
|
||||||
|
try {
|
||||||
|
ed = codec.decode(header);
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
return Optional.of("AERE PQ HEADER: block " + h + " extraData does not decode: " + e.getMessage());
|
||||||
|
}
|
||||||
|
if (!ed.getSeals().isEmpty()) {
|
||||||
|
return Optional.of(
|
||||||
|
"AERE PQ HEADER: block " + h + " carries " + ed.getSeals().size()
|
||||||
|
+ " ECDSA committed seal(s); from H_hdr " + AerePqHeaderSeal.headerBlock() + " the element MUST be empty");
|
||||||
|
}
|
||||||
|
if (ed.getPqProposerSeal().isEmpty()) {
|
||||||
|
return Optional.of("AERE PQ HEADER: block " + h + " carries no proposer seal (element 6) from H_hdr");
|
||||||
|
}
|
||||||
|
final FalconSeal ps = ed.getPqProposerSeal().get();
|
||||||
|
final Optional<Address> id = AerePqIdentity.v3IdAt(h, ps.getValidatorIndex());
|
||||||
|
if (id.isEmpty()) {
|
||||||
|
return Optional.of(
|
||||||
|
"AERE PQ HEADER: block " + h + " proposer seal index " + ps.getValidatorIndex() + " names no row of the v3 epoch in force");
|
||||||
|
}
|
||||||
|
if (!id.get().equals(header.getCoinbase())) {
|
||||||
|
return Optional.of(
|
||||||
|
"AERE PQ HEADER: block " + h + " proposer seal of index " + ps.getValidatorIndex() + " (id " + id.get()
|
||||||
|
+ ") but coinbase " + header.getCoinbase() + " - a seal cannot vouch for another proposer");
|
||||||
|
}
|
||||||
|
final Bytes32 message;
|
||||||
|
try {
|
||||||
|
message =
|
||||||
|
PqAnchor.proposalMessage(
|
||||||
|
chainId, h, ed.getRound(), new BftBlockHashing(codec).calculateDataHashForCommittedSeal(header).getBytes());
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
return Optional.of("AERE PQ HEADER: block " + h + " proposal message cannot be built: " + e.getMessage());
|
||||||
|
}
|
||||||
|
final boolean ok;
|
||||||
|
try {
|
||||||
|
ok = verifier.verify(h, ps.getValidatorIndex(), message, ps.getSignature());
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
return Optional.of("AERE PQ HEADER: block " + h + " proposer seal verification threw: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
? Optional.empty()
|
||||||
|
: Optional.of("AERE PQ HEADER: block " + h + " proposer seal of index " + ps.getValidatorIndex() + " does not verify");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includeInLightValidation() {
|
||||||
|
return belowHdr.includeInLightValidation();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Copyright contributors to Besu / AERE Network.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package org.hyperledger.besu.consensus.qbft.headervalidationrules;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqFinality;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.AerePqIdentity;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.BftBlockHashing;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.BftExtraData;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.FalconSeal;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqAnchor;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.PqRegistryV3;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.SealScheme;
|
||||||
|
import org.hyperledger.besu.consensus.common.bft.SealSchemes;
|
||||||
|
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec;
|
||||||
|
import org.hyperledger.besu.crypto.SECPSignature;
|
||||||
|
import org.hyperledger.besu.crypto.SecureRandomProvider;
|
||||||
|
import org.hyperledger.besu.cryptoservices.NodeKey;
|
||||||
|
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
|
||||||
|
import org.hyperledger.besu.datatypes.Address;
|
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||||
|
import org.hyperledger.besu.ethereum.core.Util;
|
||||||
|
import org.hyperledger.besu.ethereum.mainnet.AttachedBlockHeaderValidationRule;
|
||||||
|
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.tuweni.bytes.Bytes;
|
||||||
|
import org.apache.tuweni.bytes.Bytes32;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AIP-22 stage 5 in the Besu-derived client, with real Falcon-512 keys generated for the run: the
|
||||||
|
* seventh extraData element (the proposer's seal) round-trips canonically, stays outside BOTH hashes,
|
||||||
|
* and the header rule admits a header from H_hdr iff its ECDSA committed seals are empty and the seal
|
||||||
|
* of its coinbase's index verifies over the proposal message recomputed from the stored header. Each
|
||||||
|
* refusal is asserted BY ITS REASON. Below H_hdr the upstream rule decides. The runner's negative
|
||||||
|
* control plants "never refuse" in the rule at run time and these probes turn red.
|
||||||
|
*/
|
||||||
|
public class PqHeaderSealStage5Test {
|
||||||
|
|
||||||
|
private static final long CHAIN = 28001;
|
||||||
|
private static final long BIND = 10;
|
||||||
|
private static final long H_ID = 10;
|
||||||
|
private static final long H_FIN = 20;
|
||||||
|
private static final long H_HDR = 30;
|
||||||
|
private static final int N = 4;
|
||||||
|
private static final SealScheme FALCON = SealSchemes.FALCON_512;
|
||||||
|
private static final QbftExtraDataCodec CODEC = new QbftExtraDataCodec();
|
||||||
|
|
||||||
|
private final List<SealScheme.PrivateHandle> falcon = new ArrayList<>();
|
||||||
|
private final List<Address> ids = new ArrayList<>();
|
||||||
|
private PqHeaderSealRule rule;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void arm() {
|
||||||
|
final List<PqRegistryV3.Row> rows = new ArrayList<>();
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
final NodeKey nk = NodeKeyUtils.generate();
|
||||||
|
final SealScheme.GeneratedPair p = FALCON.generate(SecureRandomProvider.createSecureRandom());
|
||||||
|
falcon.add(p.privateKey());
|
||||||
|
final byte[] std = new byte[897];
|
||||||
|
std[0] = 0x09;
|
||||||
|
System.arraycopy(p.publicRegistryForm(), 0, std, 1, 896);
|
||||||
|
final byte[] id = PqRegistryV3.idOf(std);
|
||||||
|
final Bytes32 popMsg = PqRegistryV3.popMessage(CHAIN, BIND, N, i, id, FALCON.wireId(), std);
|
||||||
|
final byte[] pop = FALCON.sign(p.privateKey(), popMsg.toArrayUnsafe()).orElseThrow();
|
||||||
|
final TreeMap<Integer, PqRegistryV3.Key> keys = new TreeMap<>();
|
||||||
|
keys.put(FALCON.wireId() & 0xff, new PqRegistryV3.Key(FALCON, std, pop));
|
||||||
|
final PqRegistryV3.Row bare = new PqRegistryV3.Row(i, id, keys, null, Map.of(), null, null);
|
||||||
|
final SECPSignature claim = nk.sign(PqRegistryV3.claimDigest(CHAIN, PqRegistryV3.rowHash(CHAIN, BIND, N, bare)));
|
||||||
|
rows.add(
|
||||||
|
new PqRegistryV3.Row(
|
||||||
|
i, id, keys, null, Map.of(),
|
||||||
|
Util.publicKeyToAddress(nk.getPublicKey()).getBytes().toArrayUnsafe(),
|
||||||
|
claim.encodedBytes().toArrayUnsafe()));
|
||||||
|
ids.add(Address.wrap(Bytes.wrap(id)));
|
||||||
|
}
|
||||||
|
final List<Bytes32> hashes = new ArrayList<>();
|
||||||
|
for (final PqRegistryV3.Row r : rows) {
|
||||||
|
hashes.add(PqRegistryV3.rowHash(CHAIN, BIND, N, r));
|
||||||
|
}
|
||||||
|
final Bytes32 root = PqRegistryV3.merkleRoot(hashes);
|
||||||
|
final TreeMap<Long, PqRegistryV3.Registry> epochs = new TreeMap<>();
|
||||||
|
epochs.put(
|
||||||
|
BIND,
|
||||||
|
new PqRegistryV3.Registry(CHAIN, BIND, N, rows, hashes, root, PqRegistryV3.registryHash(CHAIN, BIND, N, root)));
|
||||||
|
AerePqHeaderSeal.uninstall();
|
||||||
|
AerePqFinality.uninstall();
|
||||||
|
AerePqIdentity.uninstall();
|
||||||
|
AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs, H_ID));
|
||||||
|
final AerePqFinality.SealVerifier v =
|
||||||
|
(h, i, m, s) ->
|
||||||
|
AerePqIdentity.v3KeyAt(h, i, "falcon-512")
|
||||||
|
.map(k -> FALCON.verifyRaw(k, m.toArrayUnsafe(), s.toArrayUnsafe()))
|
||||||
|
.orElse(false);
|
||||||
|
AerePqFinality.install(H_FIN, CHAIN, v);
|
||||||
|
AerePqHeaderSeal.install(H_HDR);
|
||||||
|
rule = new PqHeaderSealRule(sentinel(false), CODEC, CHAIN, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void disarm() {
|
||||||
|
AerePqHeaderSeal.uninstall();
|
||||||
|
AerePqFinality.uninstall();
|
||||||
|
AerePqIdentity.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AttachedBlockHeaderValidationRule sentinel(final boolean answer) {
|
||||||
|
return (header, parent, ctx) -> answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BftExtraData extra(final int round, final List<SECPSignature> seals, final Optional<FalconSeal> ps) {
|
||||||
|
return new BftExtraData(
|
||||||
|
Bytes.wrap(new byte[32]), seals, Optional.empty(), round, ids, Collections.emptyList(), Collections.emptyList(), ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockHeader header(final long h, final Address coinbase, final BftExtraData ed) {
|
||||||
|
return new BlockHeaderTestFixture().number(h).coinbase(coinbase).extraData(CODEC.encode(ed)).buildHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The proposer seal of {@code signer} over the header's own proposal message (for the index {@code index}). */
|
||||||
|
private Optional<FalconSeal> sealOver(final BlockHeader unsealed, final int round, final int index, final int signer) {
|
||||||
|
final Bytes32 m =
|
||||||
|
PqAnchor.proposalMessage(
|
||||||
|
CHAIN, unsealed.getNumber(), round, new BftBlockHashing(CODEC).calculateDataHashForCommittedSeal(unsealed).getBytes());
|
||||||
|
return Optional.of(new FalconSeal(index, Bytes.wrap(FALCON.sign(falcon.get(signer), m.toArrayUnsafe()).orElseThrow())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockHeader sealedHeader(final long h, final int round, final int coinbase, final int index, final int signer) {
|
||||||
|
final BlockHeader unsealed = header(h, ids.get(coinbase), extra(round, List.of(), Optional.empty()));
|
||||||
|
return header(h, ids.get(coinbase), extra(round, List.of(), sealOver(unsealed, round, index, signer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void theProposerSealIsTheSeventhElementCanonicalAndOutsideBothHashes() {
|
||||||
|
final BlockHeader unsealed = header(H_HDR, ids.get(1), extra(2, List.of(), Optional.empty()));
|
||||||
|
final BlockHeader sealed = sealedHeader(H_HDR, 2, 1, 1, 1);
|
||||||
|
final BftExtraData back = CODEC.decode(sealed);
|
||||||
|
assertThat(back.getPqProposerSeal()).isPresent();
|
||||||
|
assertThat(back.getPqProposerSeal().get().getValidatorIndex()).isEqualTo(1);
|
||||||
|
assertThat(back.getSeals()).isEmpty();
|
||||||
|
assertThat(back.getRound()).isEqualTo(2);
|
||||||
|
final BftBlockHashing hashing = new BftBlockHashing(CODEC);
|
||||||
|
assertThat(hashing.calculateHashOfBftBlockOnchain(sealed)).isEqualTo(hashing.calculateHashOfBftBlockOnchain(unsealed));
|
||||||
|
assertThat(hashing.calculateDataHashForCommittedSeal(sealed)).isEqualTo(hashing.calculateDataHashForCommittedSeal(unsealed));
|
||||||
|
assertThat(sealed.getExtraData().size()).isGreaterThan(unsealed.getExtraData().size() + 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void anEmptyCertificateSlotWithoutTheProposerSealIsNonCanonicalAndRefused() {
|
||||||
|
// six elements whose sixth is an explicit empty list: two encodings of one value, refused as before
|
||||||
|
final BytesValueRLPOutput o = new BytesValueRLPOutput();
|
||||||
|
o.startList();
|
||||||
|
o.writeBytes(Bytes.wrap(new byte[32]));
|
||||||
|
o.writeList(ids, (a, rlp) -> rlp.writeBytes(a.getBytes()));
|
||||||
|
o.writeEmptyList();
|
||||||
|
o.writeIntScalar(0);
|
||||||
|
o.writeEmptyList();
|
||||||
|
o.writeEmptyList();
|
||||||
|
o.endList();
|
||||||
|
assertThatThrownBy(() -> CODEC.decodeRaw(o.encoded())).hasMessageContaining("Non-canonical");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromHhdrAHeaderWithAVerifiedProposerSealOfItsCoinbaseAndNoEcdsaSealIsAdmitted() {
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
assertThat(rule.refusal(sealedHeader(H_HDR, 0, i, i, i))).as("proposer %s", i).isEmpty();
|
||||||
|
}
|
||||||
|
assertThat(rule.validate(sealedHeader(H_HDR + 7, 3, 2, 2, 2), null, null)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromHhdrEachWayOfBeingWrongIsRefusedByItsReason() {
|
||||||
|
// an ECDSA committed seal present
|
||||||
|
final SECPSignature ecdsa = NodeKeyUtils.generate().sign(Bytes32.ZERO);
|
||||||
|
final BlockHeader unsealed = header(H_HDR, ids.get(0), extra(0, List.of(), Optional.empty()));
|
||||||
|
final BlockHeader withEcdsa = header(H_HDR, ids.get(0), extra(0, List.of(ecdsa), sealOver(unsealed, 0, 0, 0)));
|
||||||
|
assertThat(rule.refusal(withEcdsa)).get().asString().contains("MUST be empty");
|
||||||
|
// no proposer seal
|
||||||
|
assertThat(rule.refusal(unsealed)).get().asString().contains("no proposer seal");
|
||||||
|
// index 0 sealed by key 1
|
||||||
|
assertThat(rule.refusal(sealedHeader(H_HDR, 0, 0, 0, 1))).get().asString().contains("does not verify");
|
||||||
|
// a valid seal of index 1 on a header whose coinbase is id 0
|
||||||
|
assertThat(rule.refusal(sealedHeader(H_HDR, 0, 0, 1, 1))).get().asString().contains("cannot vouch");
|
||||||
|
// an index with no row
|
||||||
|
assertThat(rule.refusal(sealedHeader(H_HDR, 0, 0, N, 0))).get().asString().contains("names no row");
|
||||||
|
// a seal over another round than the header's
|
||||||
|
final BlockHeader r1 = header(H_HDR, ids.get(0), extra(1, List.of(), Optional.empty()));
|
||||||
|
final BlockHeader sealedAtRound0 = header(H_HDR, ids.get(0), extra(1, List.of(), sealOver(unsealed, 0, 0, 0)));
|
||||||
|
assertThat(rule.refusal(sealedAtRound0)).get().asString().contains("does not verify");
|
||||||
|
assertThat(rule.refusal(header(H_HDR, ids.get(0), extra(1, List.of(), sealOver(r1, 1, 0, 0))))).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void belowHhdrTheUpstreamRuleDecidesAndHhdrNeedsHfin() {
|
||||||
|
final BlockHeader below = sealedHeader(H_HDR - 1, 0, 0, 0, 0);
|
||||||
|
assertThat(new PqHeaderSealRule(sentinel(false), CODEC, CHAIN, (h, i, m, s) -> true).validate(below, null, null)).isFalse();
|
||||||
|
assertThat(new PqHeaderSealRule(sentinel(true), CODEC, CHAIN, (h, i, m, s) -> false).validate(below, null, null)).isTrue();
|
||||||
|
assertThatThrownBy(() -> AerePqHeaderSeal.install(H_FIN - 1)).hasMessageStartingWith("AERE-PQC-HDR-01");
|
||||||
|
AerePqFinality.uninstall();
|
||||||
|
assertThatThrownBy(() -> AerePqHeaderSeal.install(H_HDR)).hasMessageStartingWith("AERE-PQC-HDR-01");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user