From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aere Network Date: Thu, 01 Oct 2026 12:18:51 +0000 Subject: [PATCH] Aere Network: AIP-22 stages 1 to 3 and the rest of the chain-2800 fleet distribution of 2026-09-29 AERE patch 0008: AIP-22 stages 1 to 3, and everything else the chain-2800 fleet runs Why this patch exists: from block 20,836,228 (AIP-22 stage 3, 2026-09-30) the header of every chain-2800 block and qbft_getValidatorsByBlockNumber name the validators by post-quantum ids (registry v3) instead of ECDSA addresses. A node built from patches 0001 to 0007 cannot read that list and stops at block 20,836,227. This patch brings the recipe to the exact source tree the chain-2800 fleet distribution of 2026-09-29 was built from, so that a node built from it follows the chain past that height. What it changes, as one delta over the tree produced by patches 0001 and 0003 to 0007: - AIP-22 stage 1 (anchor threshold schedule, at least seven of ten seals per scheme from block 20,715,632; a configuration value, read by code already in 0003) and stage 2 (a per-scheme anchor interval in aere.pq.schemeSchedule: Falcon-512 at every 32nd block and SLH-DSA-SHA2-128s at every 128th from block 20,746,736); - AIP-22 stage 3: the registry v3 format and its loader (PqRegistryV3, with its conformance vectors), post-quantum validator ids in the header, the vote tally and the validator peers (AerePqIdentity), read through aere.pq.identityBlock, aere.pq.registryV3, aere.pq.registryV3.hashes and aere.pq.registryV3.keysFrom; - the code of AIP-22 stages 4 and 5 (AerePqFinality, PqHeaderSealRule, AerePqHeaderSeal, PqCheckpointRule, the finalized tag AerePqFinalityTags). It is inactive unless its own activation properties are set; run-node.sh does not set them and chain 2800 has not activated those stages; - SLH-DSA-SHA2-128s signing split across threads and done once per round, with the same signature bytes; - blocks that carry withdrawals are rejected on BFT chains (QBFT/IBFT2 have no withdrawals); tighter bounds on consensus message lists and rounds; a gas-cap check; - the optional Block-STM parallel block processor and its wiring in MainnetProtocolSpecs, and the JSON-RPC read path that reports an unavailable state as an error instead of resolving "latest" to a null state during import. These two were the known differences between this recipe and the fleet before this revision; they are now in the recipe as well. Files that are only backups or notes in the fleet tree (*.bak-*, BAZA.txt) are not in this patch. Nothing else is left out: patches 0001 and 0003 to 0008 applied on a pristine d2032017 checkout reproduce the fleet source tree file for file, apart from the in-file licence notices described below and three lines where the fleet sources named internal machines: one comment in QbftRound (rewritten with the same number of lines, so the compiled class is identical) and the report path and header line of a benchmark test, which is not part of the distribution; and one line added to a test vector file, declaring that the 32-byte value in it is the public key of a test vector (no private key is in this patch). The upstream copyright header ("Copyright contributors to Besu" or the ConsenSys header) of every modified file is kept unchanged, per Apache License 2.0 section 4(c). Every upstream Java file this patch modifies for the first time receives, in this diff, the in-file modification notice that section 4(b) requires, naming this patch; files already modified by patches 0003 to 0007 keep the notice naming the patch that first modified them (the fleet sources of 2026-09-29 do not carry those notices; the recipe does, and this diff leaves them in place). The changed files are also listed above the diff, which is the statement of modification that section 4(b) requires. --- app/src/main/java/org/hyperledger/besu/controller/PqTipAdoptionGate.java | 278 +++++++++++ .../java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java | 110 ++++ .../java/org/hyperledger/besu/controller/PqTipAdoptionGateModeTest.java | 180 +++++++ .../main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java | 194 ++++++++ .../java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTags.java | 202 ++++++++ .../java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java | 95 ++++ .../main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java | 561 +++++++++++++++++++++ .../java/org/hyperledger/besu/consensus/common/bft/BftBlockHashing.java | 8 +- .../java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java | 7 +- .../main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java | 58 +++ .../java/org/hyperledger/besu/consensus/common/bft/FalconSealSupport.java | 206 +++++++- .../java/org/hyperledger/besu/consensus/common/bft/HybridSealProducer.java | 109 +++- .../java/org/hyperledger/besu/consensus/common/bft/HybridSealSupport.java | 90 +++- .../src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchor.java | 5 +- .../main/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapse.java | 24 +- .../java/org/hyperledger/besu/consensus/common/bft/PqRegistryHashTool.java | 14 +- .../main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3.java | 963 ++++++++++++++++++++++++++++++++++++ .../java/org/hyperledger/besu/consensus/common/bft/PqSchemeSchedule.java | 134 ++++- .../src/main/java/org/hyperledger/besu/consensus/common/bft/PqSealed.java | 61 +++ .../besu/consensus/common/bft/blockcreation/BftBlockCreator.java | 23 +- .../besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java | 9 +- .../besu/consensus/common/bft/blockcreation/BftMiningCoordinator.java | 14 +- .../besu/consensus/common/bft/blockcreation/PqAnchorProducer.java | 18 +- .../hyperledger/besu/consensus/common/bft/messagewrappers/BftMessage.java | 12 + .../org/hyperledger/besu/consensus/common/bft/network/ValidatorPeers.java | 18 +- .../java/org/hyperledger/besu/consensus/common/bft/payload/SignedData.java | 19 +- .../main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ADRS.java | 10 +- .../main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/Fors.java | 10 +- .../src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/HT.java | 22 +- .../org/hyperledger/besu/consensus/common/bft/slhdsa/IndexedDigest.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/JdkDigest.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/NodeEntry.java | 10 +- .../src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/PK.java | 10 +- .../org/hyperledger/besu/consensus/common/bft/slhdsa/ParallelSigning.java | 257 ++++++++++ .../src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_FORS.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_XMSS.java | 10 +- .../src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SK.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngine.java | 10 +- .../hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngineProvider.java | 10 +- .../besu/consensus/common/bft/slhdsa/SLHDSAKeyGenerationParameters.java | 10 +- .../besu/consensus/common/bft/slhdsa/SLHDSAKeyPairGenerator.java | 10 +- .../hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyParameters.java | 10 +- .../org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAParameters.java | 10 +- .../besu/consensus/common/bft/slhdsa/SLHDSAPrivateKeyParameters.java | 10 +- .../besu/consensus/common/bft/slhdsa/SLHDSAPublicKeyParameters.java | 10 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSASigner.java | 17 +- .../java/org/hyperledger/besu/consensus/common/bft/slhdsa/WotsPlus.java | 10 +- .../org/hyperledger/besu/consensus/common/bft/tools/PqRegistryV3Tool.java | 288 +++++++++++ .../hyperledger/besu/consensus/common/validator/blockbased/VoteTally.java | 25 + .../besu/consensus/common/validator/blockbased/VoteTallyUpdater.java | 18 +- .../java/org/hyperledger/besu/consensus/common/bft/AereKeysFromV3Test.java | 192 +++++++ .../java/org/hyperledger/besu/consensus/common/bft/AereLocalKeysV3Test.java | 164 ++++++ .../org/hyperledger/besu/consensus/common/bft/AerePqFinalityAuthorTest.java | 243 +++++++++ .../org/hyperledger/besu/consensus/common/bft/AerePqFinalityTagsTest.java | 235 +++++++++ .../java/org/hyperledger/besu/consensus/common/bft/AerePqIdentityTest.java | 135 +++++ .../org/hyperledger/besu/consensus/common/bft/HybridSealProducerTest.java | 29 ++ .../org/hyperledger/besu/consensus/common/bft/HybridSignerRegistryTest.java | 8 +- .../java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapseTest.java | 64 +-- .../test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorTest.java | 28 +- .../java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3Test.java | 509 +++++++++++++++++++ .../org/hyperledger/besu/consensus/common/bft/PqSchemeScheduleTest.java | 56 ++- .../besu/consensus/common/bft/slhdsa/SlhDsaParallelSigningTest.java | 137 +++++ .../besu/consensus/common/validator/blockbased/VoteTallyIdentityTest.java | 75 +++ consensus/common/src/test/resources/aere/registry-v3/n01-format.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n02-chain.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n03-extra-field.json | 63 +++ .../common/src/test/resources/aere/registry-v3/n05-unknown-scheme.json | 64 +++ .../common/src/test/resources/aere/registry-v3/n06-required-scheme.json | 60 +++ consensus/common/src/test/resources/aere/registry-v3/n07-falcon-header.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n08-pop.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n09-id-derivation.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n10-duplicate-id.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n11-duplicate-key.json | 62 +++ .../common/src/test/resources/aere/registry-v3/n12-rotation-one-key.json | 50 ++ .../common/src/test/resources/aere/registry-v3/n12b-rotation-wrong-key.json | 51 ++ .../common/src/test/resources/aere/registry-v3/n13-rotation-unchanged.json | 55 ++ .../common/src/test/resources/aere/registry-v3/n14-ecdsa-wrong-key.json | 62 +++ .../common/src/test/resources/aere/registry-v3/n14b-ecdsa-missing.json | 58 +++ consensus/common/src/test/resources/aere/registry-v3/n15-transport.json | 62 +++ consensus/common/src/test/resources/aere/registry-v3/n16-declared-hash.json | 63 +++ consensus/common/src/test/resources/aere/registry-v3/ok-epoch-a.json | 71 +++ .../common/src/test/resources/aere/registry-v3/ok-epoch-b-rotation.json | 60 +++ consensus/common/src/test/resources/vector-slh-dsa-noble-2026-09-03.json | 10 + .../org/hyperledger/besu/consensus/qbft/core/messagewrappers/Proposal.java | 10 +- .../hyperledger/besu/consensus/qbft/core/messagewrappers/RoundChange.java | 8 +- .../besu/consensus/qbft/core/network/QbftMessageTransmitter.java | 4 +- .../org/hyperledger/besu/consensus/qbft/core/payload/CommitPayload.java | 16 +- .../org/hyperledger/besu/consensus/qbft/core/payload/MessageFactory.java | 8 +- .../org/hyperledger/besu/consensus/qbft/core/payload/PreparePayload.java | 17 +- .../org/hyperledger/besu/consensus/qbft/core/payload/ProposalPayload.java | 17 +- .../hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java | 22 +- .../org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java | 100 +++- .../hyperledger/besu/consensus/qbft/core/statemachine/QbftRoundFactory.java | 9 +- .../org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java | 10 + .../org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java | 16 + .../hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java | 5 +- .../besu/consensus/qbft/core/validation/PqCommitEnforcement.java | 55 +- .../besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java | 181 +++++++ .../besu/consensus/qbft/core/validation/RoundChangePayloadValidator.java | 7 +- .../besu/consensus/qbft/core/messagewrappers/AereD434ListBoundsTest.java | 73 +++ .../besu/consensus/qbft/core/statemachine/PqCommitSealsSignedOnceTest.java | 210 ++++++++ .../besu/consensus/qbft/core/validation/AereD434RoundCapTest.java | 61 +++ .../besu/consensus/qbft/core/validation/AereFinalityMessageTest.java | 204 ++++++++ .../besu/consensus/qbft/core/validation/AereFinalityVectorsWriter.java | 206 ++++++++ .../besu/consensus/qbft/core/validation/AereIdentityMessageTest.java | 165 ++++++ .../besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java | 130 +++++ .../besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java | 39 ++ .../besu/consensus/qbft/core/validation/PqHybridRotationV3Test.java | 171 +++++++ .../besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java | 13 +- .../main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java | 40 +- .../hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java | 32 ++ .../org/hyperledger/besu/consensus/qbft/adaptor/QbftFinalStateImpl.java | 12 +- .../besu/consensus/qbft/headervalidationrules/PqAnchorDigestRule.java | 8 +- .../besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java | 42 +- .../besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java | 133 +++++ .../besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java | 259 ++++++++++ .../java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java | 144 ++++++ .../consensus/qbft/headervalidationrules/PqAnchorDisarmedWindowTest.java | 92 +++- .../besu/consensus/qbft/headervalidationrules/PqAnchorV2RulesTest.java | 34 ++ .../besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java | 75 +++ .../besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java | 478 ++++++++++++++++++ .../besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java | 20 +- .../hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java | 23 +- .../besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java | 20 +- .../besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java | 27 +- .../java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java | 82 ++- .../api/jsonrpc/internal/methods/AereStateUnavailableIsAnErrorTest.java | 138 ++++++ .../org/hyperledger/besu/ethereum/api/query/AereHeadStateRetryTest.java | 140 ++++++ .../java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java | 28 +- .../org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java | 6 +- .../java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsValidator.java | 37 ++ .../org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmEngine.java | 528 ++++++++++++++++++++ .../besu/ethereum/mainnet/blockstm/AereBlockStmPreprocessing.java | 119 +++++ .../hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmProcessor.java | 160 ++++++ .../java/org/hyperledger/besu/ethereum/mainnet/blockstm/BaseReader.java | 67 +++ .../java/org/hyperledger/besu/ethereum/mainnet/blockstm/BlockStmStats.java | 38 ++ .../org/hyperledger/besu/ethereum/mainnet/blockstm/FlatBaseSnapshot.java | 165 ++++++ .../main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvAccount.java | 82 +++ .../java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvBaseLayer.java | 149 ++++++ .../main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvContext.java | 55 ++ .../src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvKey.java | 156 ++++++ .../main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvMemory.java | 266 ++++++++++ .../main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/Scheduler.java | 390 +++++++++++++++ .../org/hyperledger/besu/ethereum/mainnet/AereD432GasCapUnsignedTest.java | 103 ++++ .../org/hyperledger/besu/ethereum/mainnet/AereD433BftWithdrawalsTest.java | 75 +++ .../besu/ethereum/mainnet/feemarket/LondonFeeMarketAereFloorTest.java | 129 +++++ .../besu/ethereum/mainnet/parallelization/AereBlockStmBenchmarkTest.java | 620 +++++++++++++++++++++++ .../besu/ethereum/mainnet/parallelization/AereBlockStmSmokeTest.java | 130 +++++ .../src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java | 100 +++- .../org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManager.java | 19 + .../org/hyperledger/besu/ethereum/eth/sync/PropagatedBlockAdoptionGate.java | 103 ++++ .../main/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncState.java | 47 +- .../java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java | 88 ++++ ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java | 26 + evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java | 10 +- evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java | 18 +- .../org/hyperledger/besu/evm/precompile/P256VerifyPrecompiledContract.java | 8 +- 158 files changed, 14209 insertions(+), 336 deletions(-) diff --git a/app/src/main/java/org/hyperledger/besu/controller/PqTipAdoptionGate.java b/app/src/main/java/org/hyperledger/besu/controller/PqTipAdoptionGate.java new file mode 100644 index 0000000000..5daabf7a6a --- /dev/null +++ b/app/src/main/java/org/hyperledger/besu/controller/PqTipAdoptionGate.java @@ -0,0 +1,278 @@ +/* + * 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.controller; + +import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.BftHelpers; +import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqAnchor; +import org.hyperledger.besu.consensus.common.bft.PqSealCache; +import org.hyperledger.besu.consensus.common.bft.PqSignerRegistry; +import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.eth.sync.PropagatedBlockAdoptionGate; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AERE D-412 (2026-09-26), Rule C, the consensus-aware implementation of {@link + * PropagatedBlockAdoptionGate.Gate}. + * + *

A validator does not ADOPT, as the new canonical head, a block that arrived by propagation at the + * tip of its own consensus height unless it holds a post-quantum COMMIT quorum for that exact block + * hash - the Falcon-512 commit seals it heard on AIP-21 Commit messages, re-verified now against the + * anchored registry. From H_hdr the header carries only the proposer's single seal, so a valid header no + * longer proves quorum agreement; that agreement lives in the seal cache, and a single-key forgery has + * none. The block stays VALID (this is fork choice, not validation), so: + * + *

+ * + *

The quorum proof is cryptographic: each cached seal is re-verified with Falcon over {@link + * PqAnchor#commitMessage} (the exact message a commit seal signs from the anchor form, which holds at + * every height at or above H_hdr), and only distinct verified signer indexes are counted, so junk in the + * cache cannot forge the proof. To clear the gate an adversary would need the commit quorum's distinct + * Falcon signatures over the block's commit message - which is exactly the agreement D-412 shows a single + * key cannot produce. + * + *

OBSERVE by default, since D-419 (2026-09-27). Measured on testnet 28001: validator + * aere-testnet@1 spent five seconds importing its own 3,366-transaction block, missed the commit messages of + * the next height, and this gate then refused the block the other four had decided - 226 times over 1 h 45 + * min - while its own consensus climbed to round 10 and could never decide that height again, and the sync + * downloader never engaged ({@code eth_syncing=false} 4,000 blocks behind). The third bullet above was the + * premise, and it did not hold. Meanwhile the one scenario the gate was written for had been measured on + * 2026-09-26 WITHOUT the gate: honest validators holding the honest block at the tip did not adopt the forged + * one (0 of 3), because the honest block wins the race; the real D-412 exposure is readers and catch-up, + * which Rule B1 (finality tags) and the anchor bound (PqAnchorSealsRule) cover. So the gate now defaults to + * OBSERVE: it computes exactly the same verdict and logs every block it WOULD refuse ({@value + * #OBSERVE_MARKER}), and adopts it. ENFORCE ({@value #PROPERTY_MODE}=enforce or {@value #ENV_MODE}=enforce) + * keeps the old behaviour and must be asked for; it has no release condition yet, so it can wedge a validator + * that missed a height. A safe release needs evidence one key cannot forge (a certified anchor above the + * refused block), not a timeout: a proposer that stays silent and waits for the timeout is the D-412 attack. + */ +public class PqTipAdoptionGate implements PropagatedBlockAdoptionGate.Gate { + + private static final Logger LOG = LoggerFactory.getLogger(PqTipAdoptionGate.class); + + /** System property selecting the gate mode: {@code enforce} or {@code observe} (the default). */ + public static final String PROPERTY_MODE = "aere.d412.adoptionGate"; + + /** Environment twin of {@link #PROPERTY_MODE}. */ + public static final String ENV_MODE = "AERE_D412_ADOPTION_GATE"; + + /** Logged, in observe mode, for every propagated block the enforce mode would have refused. */ + public static final String OBSERVE_MARKER = "AERE D-412 OBSERVE: would NOT adopt"; + + /** + * Whether the configuration asks for ENFORCE. Anything other than {@code enforce} (after trimming, any + * case), including nothing at all, is OBSERVE: enforcing must be an explicit request. + * + * @param property the system property value, possibly null + * @param env the environment value, possibly null + * @return true only for an explicit enforce + */ + static boolean enforceRequested(final String property, final String env) { + final String v = property != null && !property.isBlank() ? property : env; + if (v == null || v.isBlank()) { + return false; + } + final String m = v.trim().toLowerCase(java.util.Locale.ROOT); + if (!m.equals("enforce") && !m.equals("observe")) { + LOG.warn( + "AERE D-412: unknown adoption gate mode '{}' ({} / {}); using OBSERVE", m, PROPERTY_MODE, ENV_MODE); + } + return m.equals("enforce"); + } + + /** Verifies one Falcon seal over the commit message of a block; injectable for tests. */ + @FunctionalInterface + public interface SealVerifier { + /** + * Verify. + * + * @param blockNumber the height + * @param validatorIndex the signer index in the anchored registry + * @param message the commit message that was signed + * @param signature the Falcon signature + * @return true iff it verifies against the key bound at that height + */ + boolean verify(long blockNumber, int validatorIndex, Bytes message, Bytes signature); + } + + private final ValidatorProvider validatorProvider; + private final Address localEcdsaAddress; + private final long chainId; + private final PqSealCache cache; + private final SealVerifier verifier; + private final boolean enforce; + private final java.util.function.LongPredicate stage5Active; + private final java.util.concurrent.atomic.AtomicLong wouldRefuse = + new java.util.concurrent.atomic.AtomicLong(); + + /** + * Production wiring; the mode comes from {@link #PROPERTY_MODE} / {@link #ENV_MODE} (OBSERVE unless + * enforce is asked for). + * + * @param validatorProvider the validator provider (the set at a height; ids from H_id, addresses below) + * @param localEcdsaAddress this node's ECDSA address (from its node key) + * @param chainId the chain id every commit message carries + */ + public PqTipAdoptionGate( + final ValidatorProvider validatorProvider, final Address localEcdsaAddress, final long chainId) { + this( + validatorProvider, + localEcdsaAddress, + chainId, + PqSealCache.instance(), + (n, i, m, s) -> PqSignerRegistry.falconSealSupport().verifyAtHistoric(n, i, m, s), + enforceRequested(System.getProperty(PROPERTY_MODE), System.getenv(ENV_MODE)), + AerePqHeaderSeal::active); + } + + /** + * Full wiring, for tests. + * + * @param validatorProvider the validator provider + * @param localEcdsaAddress this node's ECDSA address + * @param chainId the chain id + * @param cache the seal cache to read + * @param verifier the seal verifier + * @param enforce true to refuse (ENFORCE), false to log and adopt (OBSERVE) + * @param stage5Active whether a height is at or above H_hdr (production: {@link AerePqHeaderSeal#active}) + */ + PqTipAdoptionGate( + final ValidatorProvider validatorProvider, + final Address localEcdsaAddress, + final long chainId, + final PqSealCache cache, + final SealVerifier verifier, + final boolean enforce, + final java.util.function.LongPredicate stage5Active) { + this.validatorProvider = validatorProvider; + this.localEcdsaAddress = localEcdsaAddress; + this.chainId = chainId; + this.cache = cache; + this.verifier = verifier; + this.enforce = enforce; + this.stage5Active = stage5Active; + } + + /** + * Whether this gate refuses (ENFORCE) or only logs (OBSERVE). + * + * @return true in ENFORCE mode + */ + public boolean isEnforcing() { + return enforce; + } + + /** + * How many propagated blocks this gate judged without a post-quantum commit quorum (refused in ENFORCE, + * logged and adopted in OBSERVE). + * + * @return the count since start + */ + public long wouldRefuseCount() { + return wouldRefuse.get(); + } + + @Override + public boolean mayAdopt(final BlockHeader header, final BlockHeader parent) { + final long h = header.getNumber(); + // Below H_hdr the header still carries a quorum of ECDSA committed seals, whose upstream rule + // already refuses a single-key block; nothing to add here. + if (!stage5Active.test(h)) { + return true; + } + final Collection

validators = validatorProvider.getValidatorsForBlock(parent); + if (validators == null || validators.isEmpty()) { + return true; + } + // Only a validator hears commit seals; a reader would never clear the gate, so it is not gated + // (its canonicity is Rule B). isLocal matches this node as an ECDSA address OR its v3 id, so it is + // correct below H_id (addresses) and from stage 5 (ids), where the set carries ids. + boolean localIsValidator = false; + for (final Address v : validators) { + if (AerePqIdentity.isLocal(v, localEcdsaAddress)) { + localIsValidator = true; + break; + } + } + if (!localIsValidator) { + return true; + } + final int quorum = BftHelpers.calculateRequiredValidatorQuorum(validators.size()); + if (quorum <= 0) { + return true; + } + final Bytes32 message = PqAnchor.commitMessage(chainId, h, header.getHash().getBytes()); + final List heard = cache.sealsFor(h, header.getHash()); + final Set verified = new HashSet<>(); + for (final FalconSeal seal : heard) { + if (seal == null || seal.getValidatorIndex() < 0 || seal.getSignature() == null) { + continue; + } + try { + if (verifier.verify(h, seal.getValidatorIndex(), message, seal.getSignature())) { + verified.add(seal.getValidatorIndex()); + } + } catch (final RuntimeException e) { + // a seal this node cannot judge is not counted; never a pass + LOG.debug("AERE D-412: seal of index {} for block {} could not be verified ({})", seal.getValidatorIndex(), h, e.toString()); + } + } + final boolean ok = verified.size() >= quorum; + if (ok) { + return true; + } + wouldRefuse.incrementAndGet(); + if (!enforce) { + // D-419: OBSERVE. Same verdict, recorded, and the block is adopted as before the gate existed. + LOG.info( + "{} propagated block {} at tip height {}: it holds {} verified post-quantum commit seal(s), " + + "quorum is {}; adopting it (observe mode, D-419).", + OBSERVE_MARKER, + header.getHash(), + h, + verified.size(), + quorum); + return true; + } + LOG.info( + "AERE D-412: propagated block {} at tip height {} holds {} verified post-quantum commit " + + "seal(s), quorum is {}; NOT adopting it via propagation - this node's own consensus " + + "decides this height.", + header.getHash(), + h, + verified.size(), + quorum); + return false; + } +} diff --git a/app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java b/app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java index 252f655834..afca7aca58 100644 --- a/app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java +++ b/app/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java @@ -13,40 +13,44 @@ * SPDX-License-Identifier: Apache-2.0 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.controller; import static com.google.common.base.Preconditions.checkNotNull; import org.hyperledger.besu.config.BftConfigOptions; import org.hyperledger.besu.config.BftFork; import org.hyperledger.besu.config.JsonGenesisConfigOptions; import org.hyperledger.besu.config.QbftConfigOptions; import org.hyperledger.besu.config.QbftFork; import org.hyperledger.besu.consensus.common.BftValidatorOverrides; import org.hyperledger.besu.consensus.common.EpochManager; import org.hyperledger.besu.consensus.common.ForksSchedule; +import org.hyperledger.besu.consensus.common.bft.AerePqFinality; +import org.hyperledger.besu.consensus.common.bft.AerePqFinalityTags; +import org.hyperledger.besu.consensus.common.bft.AerePqHeaderSeal; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.BftBlockInterface; import org.hyperledger.besu.consensus.common.bft.BftContext; import org.hyperledger.besu.consensus.common.bft.BftEventQueue; import org.hyperledger.besu.consensus.common.bft.BftExecutors; import org.hyperledger.besu.consensus.common.bft.BftProcessor; import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule; import org.hyperledger.besu.consensus.common.bft.BftRoundExpiryTimeCalculator; import org.hyperledger.besu.consensus.common.bft.BlockTimer; import org.hyperledger.besu.consensus.common.bft.EthSynchronizerUpdater; import org.hyperledger.besu.consensus.common.bft.EventMultiplexer; import org.hyperledger.besu.consensus.common.bft.FalconSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSealSupport; import org.hyperledger.besu.consensus.common.bft.MessageTracker; import org.hyperledger.besu.consensus.common.bft.PqAnchorConfig; import org.hyperledger.besu.consensus.common.bft.PqAnchorSyncModeGuard; import org.hyperledger.besu.consensus.common.bft.PqAnchorThresholdGuard; import org.hyperledger.besu.consensus.common.bft.PqRegistryHash; import org.hyperledger.besu.consensus.common.bft.PqSealCache; import org.hyperledger.besu.consensus.common.bft.PqSignerRegistry; import org.hyperledger.besu.consensus.common.bft.RoundTimer; @@ -71,61 +75,63 @@ import org.hyperledger.besu.consensus.qbft.adaptor.QbftBlockCreatorFactoryAdapto import org.hyperledger.besu.consensus.qbft.adaptor.QbftBlockInterfaceAdaptor; import org.hyperledger.besu.consensus.qbft.adaptor.QbftBlockchainAdaptor; import org.hyperledger.besu.consensus.qbft.adaptor.QbftFinalStateImpl; import org.hyperledger.besu.consensus.qbft.adaptor.QbftProtocolScheduleAdaptor; import org.hyperledger.besu.consensus.qbft.adaptor.QbftValidatorModeTransitionLoggerAdaptor; import org.hyperledger.besu.consensus.qbft.adaptor.QbftValidatorProviderAdaptor; import org.hyperledger.besu.consensus.qbft.blockcreation.QbftBlockCreatorFactory; import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory; import org.hyperledger.besu.consensus.qbft.core.statemachine.QbftBlockHeightManagerFactory; import org.hyperledger.besu.consensus.qbft.core.statemachine.QbftController; import org.hyperledger.besu.consensus.qbft.core.statemachine.QbftRoundFactory; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockInterface; import org.hyperledger.besu.consensus.qbft.core.types.QbftEventHandler; import org.hyperledger.besu.consensus.qbft.core.types.QbftFinalState; import org.hyperledger.besu.consensus.qbft.core.types.QbftMessage; import org.hyperledger.besu.consensus.qbft.core.types.QbftMinedBlockObserver; import org.hyperledger.besu.consensus.qbft.core.types.QbftProtocolSchedule; import org.hyperledger.besu.consensus.qbft.core.types.QbftValidatorProvider; import org.hyperledger.besu.consensus.qbft.core.validation.MessageValidatorFactory; +import org.hyperledger.besu.consensus.qbft.core.validation.PqFinalityLayersGuard; import org.hyperledger.besu.consensus.qbft.jsonrpc.QbftJsonRpcMethods; import org.hyperledger.besu.consensus.qbft.network.QbftGossiperImpl; import org.hyperledger.besu.consensus.qbft.protocol.Istanbul100SubProtocol; import org.hyperledger.besu.consensus.qbft.validator.ForkingValidatorProvider; import org.hyperledger.besu.consensus.qbft.validator.TransactionValidatorProvider; import org.hyperledger.besu.consensus.qbft.validator.ValidatorContractController; import org.hyperledger.besu.consensus.qbft.validator.ValidatorModeTransitionLogger; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethods; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.MinedBlockObserver; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.SnapProtocol; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager; +import org.hyperledger.besu.ethereum.eth.sync.PropagatedBlockAdoptionGate; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.util.Subscribers; import java.time.Duration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** The Qbft Besu controller builder. */ public class QbftBesuControllerBuilder extends BesuControllerBuilder { @@ -416,40 +422,101 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder { .getChainId() .map(java.math.BigInteger::longValue) .orElse(0L), pqRegistrySchedule); // AERE SINCRONIZARE (2026-08-02): the anchor is only a defence if it applies to the history this // node ACCEPTS, and not only to the blocks it is offered as proposals. Outside FULL sync the // header import path never instantiates a header validator at all, so both anchor rules would be // skipped for the entire span between checkpoint and pivot. Refuse to start rather than run a // node that believes it is protected and is not. // // Placed with the other two startup guards and for the same reason: a chain head exists here, the // network is not up and the QBFT state machine has not been created, so this is a clean refusal // to start. Inert when aere.pq.anchorBlock is unset, which is chain 2800 as it stands today. // AERE GARDA-PRAG (2026-08-02): read the anchor configuration ONCE, here, and hand the SAME // object to every guard that needs it. Two calls to fromSystemConfiguration would print the // ARMED banner twice and, worse, would give two guards two independent chances to disagree // about what is actually configured on this node. final PqAnchorConfig aereAnchorConfig = PqAnchorConfig.fromSystemConfiguration(); + // AERE AIP-22 stage 3: the post-quantum identity switch, read ONCE here beside the anchor + // configuration; the schemes every v3 row must carry are the ones the scheme schedule names at + // H_id. Inert when aere.pq.identityBlock is unset. + AerePqIdentity.installFromSystemProperties( + aereAnchorConfig.chainId(), + hId -> + HybridSealSupport.instance() + .schedule() + .map(s -> (java.util.List) new java.util.ArrayList<>(s.schemesAt(hId))) + .orElse(java.util.List.of("falcon-512"))); + // AERE AIP-22 stage 3b: at H_k the SOURCE of the keys that seal moves to the v3 epochs; the continuity + // guard proves, before the node joins anything, that no key moves with it (every key of the epoch at + // H_k equals the key the existing registries give at H_k - 1). Inert when H_k is not configured. + AerePqIdentity.verifyKeyContinuity( + (index, scheme) -> { + final long before = + AerePqIdentity.installed() + .map(c -> c.keysFrom() - 1L) + .orElseThrow(() -> new IllegalStateException("identity not installed")); + return "falcon-512".equals(scheme) + ? FalconSealSupport.instance().publicKeyBytesAtHistoric(before, index) + : HybridSealSupport.instance().registry().flatMap(r -> r.publicKey(index, scheme)); + }); + // AERE AIP-22 stage 3b-ii: in every v3 epoch from H_k where this node is a registered validator it must + // hold its row's keys, or it would go mute at that epoch; refuse to start instead. Inert without H_k. + FalconSealSupport.instance() + .verifyLocalKeysCoverV3EpochsOrAbort(Util.publicKeyToAddress(nodeKey.getPublicKey())); + HybridSealSupport.instance() + .verifyLocalKeysCoverV3EpochsOrAbort(Util.publicKeyToAddress(nodeKey.getPublicKey())); + // AERE AIP-22 stage 4: H_fin (aere.pq.finalityBlock), from which a message's author is the id of its post-quantum seal's + // index once that seal verifies, and the envelope's ECDSA is not read; refuses unless the keys are read from the v3 epochs + // at or below it (AERE-PQC-FIN-01). Inert when unset, which is every chain today. + AerePqFinality.installFromSystemProperties(aereAnchorConfig.chainId()); + // ...and the four seal layers must be armed at or below it (AERE-PQC-FIN-02): the commit layer is the one that judges a + // COMMIT's seal (its author is bound to the commit seal's signer), and the layers stay the second judge of the rest. + PqFinalityLayersGuard.verifyOrAbort(AerePqFinality.finalityBlock()); + // ...and a node that seals must seal all four messages from H_fin, or it would be mute there (AERE-PQC-FIN-03) + PqFinalityLayersGuard.verifyEmissionOrAbort( + AerePqFinality.finalityBlock(), + FalconSealSupport.instance().signingEnabled(), + FalconSealSupport.instance().attachBlock(), + FalconSealSupport.proposalAttachBlock(), + FalconSealSupport.prepareAttachBlock(), + 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) { + 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" + + " envelope's ECDSA is not read (four seal layers armed at or below it, this node seals: {})", + AerePqFinality.finalityBlock(), + FalconSealSupport.instance().signingEnabled()); + } + PqAnchorSyncModeGuard.verifySyncModeOrAbort( syncConfig == null || syncConfig.getSyncMode() == null ? null : syncConfig.getSyncMode().name(), aereAnchorConfig); // AERE PERSISTENTA-SIGILII (2026-08-05): THE SECOND HALF of the chain death on a whole-fleet // restart. The first half is repaired above (BLOCAJ-REPORNIRE): the registry now arms at startup // from the chain head state, and all seven nodes reported SUCCEEDED. The chain died anyway, the // refusal only changing shape: "registry address-bound=TRUE ... Heard 0 seal(s)", frozen 150 s // on the first restart and 298 s on the second. // // WHY. Falcon seals over M(head) travel ONLY on the Commit messages of the head block. Those // messages are never repeated after a restart, and they exist nowhere else: the head block // carries in element 6 the certificate over its PARENT, not over itself. So every node started // with zero seals, nobody reached K, nobody could propose, and so nobody sent another Commit. // The same circular deadlock, one level down. // // WHY THIS IS NOT A8 IN NEW CLOTHES, and this is the whole security argument: the seal is // SELF-VERIFYING. Every seal read from the file is cryptographically verified again against the @@ -526,40 +593,83 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder { transactionPool, protocolContext, bftProtocolSchedule, qbftForksSchedule, miningConfiguration, localAddress, qbftExtraDataCodec, ethProtocolManager.ethContext().getScheduler()); final ValidatorProvider validatorProvider; if (qbftConfig.getStartBlock().isPresent()) { validatorProvider = protocolContext .getConsensusContext(BftContext.class, qbftConfig.getStartBlock().getAsLong()) .getValidatorProvider(); } else { validatorProvider = protocolContext.getConsensusContext(BftContext.class).getValidatorProvider(); } + // AERE D-412 (2026-09-26), Rule C: arm the propagation adoption gate. From AIP-22 stage 5 a valid + // header carries only the proposer's single Falcon seal, so it no longer proves a quorum agreed the + // block; a single key could forge one and, until this gate, honest validators adopted it via NewBlock + // and built on top (measured on rep28098). The gate lets this node refuse to ADOPT such a propagated + // block at the tip of its own consensus height unless it holds a post-quantum commit quorum for it + // (heard on AIP-21 Commit seals). Block VALIDITY is unchanged (sync still imports it); only + // ADOPTION-at-the-tip is gated, only for a validator, only from H_hdr. Inert here on any chain whose + // H_hdr is unset, which is chain 2800 today. Placed after the validator set is resolved (localAddress + // and validatorProvider both exist) and before the network and the QBFT machine start. + // D-419 (2026-09-27): the gate defaults to OBSERVE (logs every block it would refuse, adopts it); ENFORCE + // wedged a validator that missed one height's commits on testnet 28001 and must be asked for explicitly. + if (aereAnchorConfig.everActive()) { + final PqTipAdoptionGate tipGate = + new PqTipAdoptionGate(validatorProvider, localAddress, aereAnchorConfig.chainId()); + PropagatedBlockAdoptionGate.install(tipGate); + LOG.info( + "AERE D-412: propagation adoption gate armed in {} mode; a propagated block at the tip {} " + + "without a post-quantum commit quorum from H_hdr={} (unset means never on this node).", + tipGate.isEnforcing() ? "ENFORCE" : "OBSERVE", + tipGate.isEnforcing() ? "is NOT adopted" : "is adopted and logged", + AerePqHeaderSeal.headerBlock()); + } + + // AERE D-412 (2026-09-26), Rule B1: post-quantum FINALITY TAGS (see AerePqFinalityTags). finalized/safe = the parent of the + // last canonical anchor that carries a certificate (v1 Falcon list OR v2 scheme-tagged certificate); monotonic; never moved + // back by a reorg (AERE-PQC-FIN-01). No validity, fork-choice or import is touched. Proven live on rep28098 2026-09-26. + if (aereAnchorConfig.everActive()) { + final QbftExtraDataCodec codecForTags = qbftExtraDataCodec; + AerePqFinalityTags.install( + aereAnchorConfig, + blockchain, + h -> { + try { + return AerePqFinalityTags.carriesCertificate(codecForTags.decodeRaw(h.getExtraData())); + } catch (final RuntimeException e) { + return false; // an undecodable header carries nothing this rule can rely on + } + }); + LOG.info( + "AERE D-412 B1: finalized/safe tags follow the last certified anchor's parent (interval {} at the head).", + aereAnchorConfig.intervalAt(blockchain.getChainHeadBlockNumber())); + } + // AERE GARDA-PRAG (2026-08-02): refuse to start on a seal threshold that STOPS the chain. // // Measured before this line was written: nothing anywhere refused a threshold value. Searched // the whole fork tree for any occurrence of minSeals near quorum, throw, refuse, abort or // Illegal, and there were ZERO. PqAnchorConfig rejects a negative K and a step scheduled below // the activation height, and nothing else; the only other check, PqAnchorProducer's // warnIfActivationHeightCanRefuse, writes one LOG.error and returns. So a value at or above the // QBFT quorum was a configuration a node accepted in silence and then halted on. // // Placed here rather than beside the other three startup guards because this is the FIRST point // at which the validator set is resolved, and the bound is a function of its size. It is still // before the network exists (ValidatorPeers is built below) and before the QBFT state machine is // created, so a refusal here is a clean refusal to start rather than a mid-flight halt. PqAnchorThresholdGuard.verifyThresholdAgainstQuorumOrAbort( aereAnchorConfig, validatorProvider.getValidatorsAtHead().size()); final QbftValidatorProvider qbftValidatorProvider = new QbftValidatorProviderAdaptor(validatorProvider); final QbftBlockInterface qbftBlockInterface = new QbftBlockInterfaceAdaptor(bftBlockInterface); diff --git a/app/src/test/java/org/hyperledger/besu/controller/PqTipAdoptionGateModeTest.java b/app/src/test/java/org/hyperledger/besu/controller/PqTipAdoptionGateModeTest.java new file mode 100644 index 0000000000..1e87f3b984 --- /dev/null +++ b/app/src/test/java/org/hyperledger/besu/controller/PqTipAdoptionGateModeTest.java @@ -0,0 +1,180 @@ +/* + * 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.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqSealCache; +import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * D-419 (2026-09-27): the Rule C tip adoption gate in its two modes. OBSERVE (the default) computes the same + * verdict as ENFORCE, counts the blocks it would refuse, and adopts them; ENFORCE refuses. Every case has its + * opposite: the same block without a quorum is refused under ENFORCE and adopted under OBSERVE, and a quorum of + * seals that do NOT verify is refused under ENFORCE, so the gate verifies seals and does not merely count them. + */ +class PqTipAdoptionGateModeTest { + + private static final long H_HDR = 100L; + private static final long CHAIN_ID = 28098L; + private static final Address LOCAL = Address.fromHexString("0x00000000000000000000000000000000000000a1"); + private static final List
SET = + List.of( + LOCAL, + Address.fromHexString("0x00000000000000000000000000000000000000a2"), + Address.fromHexString("0x00000000000000000000000000000000000000a3"), + Address.fromHexString("0x00000000000000000000000000000000000000a4")); + // a distinct timestamp per header, so every test reads its own seal-cache entry from the shared singleton + private static final AtomicLong TS = new AtomicLong(1_900_000_000L); + + private ValidatorProvider provider; + + @BeforeEach + void setUp() { + // no v3 identity installed: isLocal falls back to address equality, which is what this set uses + AerePqIdentity.uninstall(); + provider = mock(ValidatorProvider.class); + when(provider.getValidatorsForBlock(any())).thenReturn(SET); + } + + @AfterEach + void tearDown() { + AerePqIdentity.uninstall(); + } + + private static BlockHeader header(final long number) { + return new BlockHeaderTestFixture().number(number).timestamp(TS.incrementAndGet()).buildHeader(); + } + + private PqTipAdoptionGate gate(final boolean enforce, final boolean sealsVerify) { + return new PqTipAdoptionGate( + provider, + LOCAL, + CHAIN_ID, + PqSealCache.instance(), + (n, i, m, s) -> sealsVerify, + enforce, + h -> h >= H_HDR); + } + + private static void recordSeals(final BlockHeader h, final int count) { + final List seals = + java.util.stream.IntStream.range(0, count) + .mapToObj(i -> new FalconSeal(i, Bytes.of(i + 1))) + .toList(); + PqSealCache.instance().record(h.getNumber(), h.getHash(), seals); + } + + @Test + void enforceRefusesATipBlockWithoutAQuorum() { + final BlockHeader h = header(150); + final PqTipAdoptionGate g = gate(true, true); + assertThat(g.mayAdopt(h, header(149))).isFalse(); + assertThat(g.wouldRefuseCount()).isEqualTo(1); + } + + @Test + void observeAdoptsTheSameBlockAndCountsIt() { + final BlockHeader h = header(150); + final PqTipAdoptionGate g = gate(false, true); + assertThat(g.mayAdopt(h, header(149))).isTrue(); + assertThat(g.wouldRefuseCount()).isEqualTo(1); + } + + @Test + void enforceAdoptsWithAVerifiedQuorum() { + final BlockHeader h = header(151); + recordSeals(h, 3); // quorum of 4 validators is 3 + final PqTipAdoptionGate g = gate(true, true); + assertThat(g.mayAdopt(h, header(150))).isTrue(); + assertThat(g.wouldRefuseCount()).isZero(); + } + + @Test + void enforceRefusesAQuorumOfSealsThatDoNotVerify() { + final BlockHeader h = header(152); + recordSeals(h, 4); + final PqTipAdoptionGate g = gate(true, false); + assertThat(g.mayAdopt(h, header(151))).isFalse(); + assertThat(g.wouldRefuseCount()).isEqualTo(1); + } + + @Test + void belowHdrNeitherModeGatesOrCounts() { + final BlockHeader h = header(H_HDR - 1); + final PqTipAdoptionGate e = gate(true, true); + final PqTipAdoptionGate o = gate(false, true); + assertThat(e.mayAdopt(h, header(H_HDR - 2))).isTrue(); + assertThat(o.mayAdopt(h, header(H_HDR - 2))).isTrue(); + assertThat(e.wouldRefuseCount()).isZero(); + assertThat(o.wouldRefuseCount()).isZero(); + } + + @Test + void aNodeOutsideTheSetIsNotGatedEvenInEnforce() { + when(provider.getValidatorsForBlock(any())).thenReturn(SET.subList(1, 4)); + final PqTipAdoptionGate g = gate(true, true); + assertThat(g.mayAdopt(header(153), header(152))).isTrue(); + assertThat(g.wouldRefuseCount()).isZero(); + } + + @Test + void enforceMustBeAskedForExplicitly() { + assertThat(PqTipAdoptionGate.enforceRequested(null, null)).isFalse(); + assertThat(PqTipAdoptionGate.enforceRequested("", " ")).isFalse(); + assertThat(PqTipAdoptionGate.enforceRequested("observe", null)).isFalse(); + assertThat(PqTipAdoptionGate.enforceRequested("bogus", null)).isFalse(); + assertThat(PqTipAdoptionGate.enforceRequested("enforce", null)).isTrue(); + assertThat(PqTipAdoptionGate.enforceRequested(" ENFORCE ", null)).isTrue(); + assertThat(PqTipAdoptionGate.enforceRequested(null, "enforce")).isTrue(); + // the system property wins over the environment when both are set + assertThat(PqTipAdoptionGate.enforceRequested("observe", "enforce")).isFalse(); + } + + @Test + void theProductionConstructorDefaultsToObserve() { + final String before = System.getProperty(PqTipAdoptionGate.PROPERTY_MODE); + try { + System.clearProperty(PqTipAdoptionGate.PROPERTY_MODE); + if (System.getenv(PqTipAdoptionGate.ENV_MODE) == null) { + assertThat(new PqTipAdoptionGate(provider, LOCAL, CHAIN_ID).isEnforcing()).isFalse(); + } + System.setProperty(PqTipAdoptionGate.PROPERTY_MODE, "enforce"); + assertThat(new PqTipAdoptionGate(provider, LOCAL, CHAIN_ID).isEnforcing()).isTrue(); + } finally { + if (before == null) { + System.clearProperty(PqTipAdoptionGate.PROPERTY_MODE); + } else { + System.setProperty(PqTipAdoptionGate.PROPERTY_MODE, before); + } + } + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java new file mode 100644 index 0000000000..64dec0d1e1 --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinality.java @@ -0,0 +1,194 @@ +/* + * 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; + +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.datatypes.Address; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +/** + * AERE AIP-22 stage 4 (plan 9.6.8): from H_fin ({@code aere.pq.finalityBlock}) the ECDSA signature of a + * consensus message's envelope is no longer a criterion. The author of a message is the post-quantum id + * of the row its seal's index names in the v3 epoch in force at the message's height, and ONLY once that + * seal VERIFIES: a PROPOSAL, PREPARE or ROUND-CHANGE seal over its own domain, through the same verifier + * the seal layers use; a COMMIT's index only when it names the id of the commit seal's signer (the commit + * layer, armed at or below H_fin by AERE-PQC-FIN-02, judges the seal itself). The envelope's recovered + * ECDSA address is not read for authorship, so a validator may sign the envelope with any secp256k1 key. + * Anything else at or above H_fin has no author ({@link #NO_AUTHOR}, never in a set). + * + *

WHY HERE AND NOT ONLY IN THE LAYERS (read in the code 2026-09-23 ~18:02Z): QbftController relays a message + * as soon as its author is in the set, BEFORE the layers judge it. An author read from an unverified + * index would let anyone make every validator relay garbage in any validator's name; before H_fin that + * needed a validator's ECDSA key. The layers keep judging exactly as before. + * + *

Requires the post-quantum identity (stage 3) and the keys read from the v3 epochs (stage 3b): + * H_fin must be at or above H_k, else startup is refused (AERE-PQC-FIN-01). Unset: never, which is + * every chain today. + */ +public final class AerePqFinality { + + /** The system property naming H_fin. */ + public static final String PROPERTY = "aere.pq.finalityBlock"; + + /** The author of a message that cannot have one from H_fin (no seal, or an index with no row). */ + public static final Address NO_AUTHOR = Address.ZERO; + + /** Verifies a Falcon seal of a registry index at a height (the seal layers' verifier in production). */ + @FunctionalInterface + public interface SealVerifier { + /** + * Whether the seal verifies. + * + * @param height the message's height + * @param index the seal's registry index + * @param message the message the seal signs + * @param signature the seal's signature + * @return true when it verifies under that index's key + */ + boolean verify(long height, int index, Bytes message, Bytes signature); + } + + private static volatile long finalityBlock = AerePqIdentity.NEVER; + private static volatile long chainId; + private static volatile SealVerifier verifier = (h, i, m, s) -> false; + + private AerePqFinality() {} + + /** + * Arms H_fin (tests and the startup path). + * + * @param hFin H_fin, or {@link AerePqIdentity#NEVER} + * @param chain the chain id every seal message carries + * @param sealVerifier the verifier of the seal layers + */ + public static void install(final long hFin, final long chain, final SealVerifier sealVerifier) { + if (hFin != AerePqIdentity.NEVER) { + final long keysFrom = + AerePqIdentity.installed().map(AerePqIdentity.Config::keysFrom).orElse(AerePqIdentity.NEVER); + if (keysFrom == AerePqIdentity.NEVER || hFin < keysFrom) { + throw new IllegalStateException( + "AERE-PQC-FIN-01: " + + PROPERTY + + "=" + + hFin + + " needs the post-quantum identity and the keys read from the v3 epochs (aere.pq.registryV3.keysFrom)" + + " at or below it (keysFrom " + + (keysFrom == AerePqIdentity.NEVER ? "unset" : Long.toString(keysFrom)) + + ")"); + } + if (chain <= 0 || sealVerifier == null) { + throw new IllegalStateException( + "AERE-PQC-FIN-01: " + + PROPERTY + + "=" + + hFin + + " needs the chain id (aere.pq.chainId) and a seal verifier: every author it gives is a verified seal"); + } + } + finalityBlock = hFin; + chainId = chain; + verifier = sealVerifier == null ? (h, i, m, s) -> false : sealVerifier; + } + + /** + * H_fin as armed on this node. + * + * @return H_fin, or {@link AerePqIdentity#NEVER} when unset + */ + public static long finalityBlock() { + return finalityBlock; + } + + /** Disarms (tests). */ + public static void uninstall() { + finalityBlock = AerePqIdentity.NEVER; + chainId = 0; + verifier = (h, i, m, s) -> false; + } + + /** + * Arms from {@link #PROPERTY}; unset means never. Call after the identity is installed. + * + * @param chain the chain id of the anchor configuration (aere.pq.chainId) + */ + public static void installFromSystemProperties(final long chain) { + 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-FIN-01: " + PROPERTY + "='" + v + "' is not a height", e); + } + final PqSignerRegistry layers = PqSignerRegistry.falconSealSupport(); + install(h, chain, layers::verifyAtOwnHead); + } + + /** + * Whether the post-quantum seal is the author of messages at this height. + * + * @param height the message's height + * @return true from H_fin when armed + */ + public static boolean active(final long height) { + return height >= finalityBlock; + } + + /** + * The author of a message from H_fin: the id of its seal's index at the message's height, once the + * seal verifies (a COMMIT: once the index names the commit seal signer's id). + * + * @param payload the message payload + * @param height the message's height + * @return the id, or {@link #NO_AUTHOR} + */ + public static Address authorFromSeal(final Payload payload, final long height) { + if (!(payload instanceof PqSealed sealed)) { + return NO_AUTHOR; + } + final Optional seal = sealed.getFalconSeal(); + if (seal.isEmpty()) { + return NO_AUTHOR; + } + final int index = seal.get().getValidatorIndex(); + final Optional

id = AerePqIdentity.v3IdAt(height, index); + if (id.isEmpty()) { + return NO_AUTHOR; + } + try { + final Optional
commitSigner = sealed.commitSealSigner(); + if (commitSigner.isPresent()) { + return id.get().equals(AerePqIdentity.author(commitSigner.get(), height)) ? id.get() : NO_AUTHOR; + } + final Optional message = sealed.pqSealMessage(chainId); + if (message.isEmpty()) { + return NO_AUTHOR; + } + return verifier.verify(height, index, message.get(), seal.get().getSignature()) + ? id.get() + : NO_AUTHOR; + } catch (final RuntimeException e) { + // a seal we cannot judge authors nothing: "cannot judge" is never a pass + return NO_AUTHOR; + } + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTags.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTags.java new file mode 100755 index 0000000000..e5e11c939f --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTags.java @@ -0,0 +1,202 @@ +/* + * 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; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +// in this Besu base (26.4.0-dev, d2032017b) the event type enum lives in the plugin API, not nested in BlockAddedEvent +// (measured 2026-09-26: "cannot find symbol EventType in class BlockAddedEvent" on the first harness run) +import org.hyperledger.besu.plugin.data.AddedBlockContext.EventType; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Predicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AERE D-412 Rule B1 (2026-09-26): the post-quantum FINALITY TAGS. + * + *

What this closes, read in the code and measured on the chain. From AIP-22 stage 5 a valid + * between-anchor header carries only the proposer's Falcon-512 seal, so a single validator key can + * forge a header a reader imports as valid and, if that reader is syncing or eclipsed, adopts as its + * canonical tip (D-412, corrected severity). Such a forged tail is bounded: at the next anchor height + * {@code PqAnchorSealsRule} (R2, FULL validation, which {@code PqAnchorSyncModeGuard} makes the only + * sync mode) requires K distinct valid Falcon-512 seals over the parent's commit message, which one key + * cannot produce, so the tail dies within one anchor interval. What remains is that the reader's + * {@code latest} can transiently name a forged block, and a consumer who asks for {@code finalized} or + * {@code safe} - the tags bridges, exchanges and rollups use - received, on every node of chain 2800 and + * 28001 measured 2026-09-26, the error {@code Unknown block}: QBFT never sets them. + * + *

The rule. {@code finalized} and {@code safe} name the last block COVERED BY A VALID ANCHOR + * CERTIFICATE: when a canonical head at an anchor height carries a certificate, the anchor's PARENT + * becomes finalized (the certificate's K seals sign the parent's commit message, AIP-15, and R1/R2 have + * already refused the header if that certificate does not bind or does not verify). Between anchors + * the tags do not move. A forged between-anchor block can therefore never be reported final: it is + * never an anchor's certified parent. This is exactly the finality AIP-22 section 1 defines ("the + * finality of the block is established only by the covering anchor"), exposed on the API the world + * already reads. + * + *

What it deliberately does NOT do. It does not touch fork-choice, block validity or import: + * nothing here can stop the chain or refuse a block, and a node that lags still catches up exactly as + * before. It only writes the two tags, monotonically: a later event can only move them forward. If a + * canonical reorg ever drops below the finalized block (which would mean K validators signed two + * conflicting anchors, outside this rule's adversary), it is logged as {@code AERE-PQC-FIN-01} at + * ERROR and the tags are left where they were, never moved back in silence. + * + *

At startup the tags are initialised by scanning back from the head to the most recent + * anchor height that carries a certificate (at most two intervals), so a freshly started node answers + * {@code finalized} before it imports its next anchor. + * + *

Inert on a chain where the anchor was never active (the node never installs it); inert below + * the activation height (no anchor height applies there). Stated, not measured here: the behaviour on + * the live fleet, which is the rehearsal's job. + */ +public final class AerePqFinalityTags { + + private static final Logger LOG = LoggerFactory.getLogger(AerePqFinalityTags.class); + + /** Logged when a canonical reorg would move finality backwards; the tags are not moved. */ + public static final String CODE_REORG_BELOW_FINALIZED = "AERE-PQC-FIN-01"; + + private final PqAnchorConfig config; + private final MutableBlockchain blockchain; + private final Predicate carriesCertificate; + private final AtomicLong finalizedNumber = new AtomicLong(-1L); + + AerePqFinalityTags( + final PqAnchorConfig config, + final MutableBlockchain blockchain, + final Predicate carriesCertificate) { + this.config = config; + this.blockchain = blockchain; + this.carriesCertificate = carriesCertificate; + } + + /** + * Install the tags on a blockchain: initialise from the current head, then follow every block added. + * + * @param config the anchor configuration (activation height, interval schedule) + * @param blockchain the chain whose finalized/safe tags are written + * @param carriesCertificate whether a header carries a (validated) anchor certificate + * @return the installed instance, for inspection + */ + public static AerePqFinalityTags install( + final PqAnchorConfig config, + final MutableBlockchain blockchain, + final Predicate carriesCertificate) { + final AerePqFinalityTags tags = new AerePqFinalityTags(config, blockchain, carriesCertificate); + tags.initialiseFromHead(); + blockchain.observeBlockAdded(tags::onBlockAdded); + return tags; + } + + /** The height currently reported as finalized, or -1 when none has been set by this rule. */ + public long finalizedNumber() { + return finalizedNumber.get(); + } + + /** + * Whether a decoded header carries an anchor certificate. A header carries the v1 Falcon list OR + * the v2 scheme-tagged certificate, never both ({@link BftExtraData}), and from AIP-22 stage 5 + * every anchor carries the v2 certificate while {@code getFalconSeals()} is empty. So a predicate + * that only looked at the v1 list ({@code getFalconSeals()}) never fired on a stage-5 chain - + * measured 2026-09-26 on the rep28098 rehearsal, where {@code finalized} stayed 0 while the chain + * ran past certified anchors 1920 and 1952. Both fields are checked here, in one place both the + * controller and the test read, so the two cannot drift. + * + * @param extraData the decoded BFT extra data of the header + * @return true when the header carries a (v1 or v2) anchor certificate + */ + public static boolean carriesCertificate(final BftExtraData extraData) { + return !extraData.getFalconSeals().isEmpty() || !extraData.getHybridSeals().isEmpty(); + } + + /** Scan back from the head, at most two anchor intervals, to the last certified anchor. */ + void initialiseFromHead() { + final BlockHeader head = blockchain.getChainHeadHeader(); + if (head == null) { + return; + } + final long top = head.getNumber(); + final long floor = Math.max(0L, top - 2L * Math.max(1, config.intervalAt(top))); + for (long h = top; h >= floor; h--) { + if (!config.anchorAppliesAt(h)) { + continue; + } + final Optional header = blockchain.getBlockHeader(h); + if (header.isPresent() && carriesCertificate.test(header.get())) { + set(header.get().getParentHash(), h - 1L, "startup scan"); + return; + } + } + LOG.info( + "AERE PQ FINALITY (B1): no certified anchor between {} and the head {}; finalized/safe stay " + + "unset until the next anchor is imported.", + floor, + top); + } + + /** + * Follow a block added to the chain. + * + * @param event the event + */ + void onBlockAdded(final BlockAddedEvent event) { + if (event == null || !event.isNewCanonicalHead()) { + return; + } + final BlockHeader header = event.getHeader(); + if (header == null) { + return; + } + if (event.getEventType() == EventType.CHAIN_REORG && finalizedNumber.get() >= 0) { + final Hash ancestorHash = event.getCommonAncestorHash(); + final Optional ancestor = + ancestorHash == null ? Optional.empty() : blockchain.getBlockHeader(ancestorHash); + if (ancestor.isPresent() && ancestor.get().getNumber() < finalizedNumber.get()) { + LOG.error( + "{}: a canonical reorg to {} (common ancestor {}) went BELOW the finalized block {}. " + + "Finality tags are NOT moved back. Under the anchor rules this needs K validators to " + + "have certified two conflicting anchors; investigate before anything else.", + CODE_REORG_BELOW_FINALIZED, + header.getNumber(), + ancestor.get().getNumber(), + finalizedNumber.get()); + } + } + final long h = header.getNumber(); + if (config.anchorAppliesAt(h) && carriesCertificate.test(header)) { + set(header.getParentHash(), h - 1L, "anchor " + h); + } + } + + private void set(final Hash finalHash, final long finalNumber, final String why) { + if (finalNumber <= finalizedNumber.get()) { + return; // monotonic: never backwards, never twice for the same height + } + blockchain.setFinalized(finalHash); + blockchain.setSafeBlock(finalHash); + finalizedNumber.set(finalNumber); + LOG.info( + "AERE PQ FINALITY (B1): finalized/safe = {} ({}), covered by the certificate of {}.", + finalNumber, + finalHash, + why); + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java new file mode 100644 index 0000000000..4f3a7c76c0 --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqHeaderSeal.java @@ -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; + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java new file mode 100644 index 0000000000..99778b62ab --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentity.java @@ -0,0 +1,561 @@ +/* + * AERE AIP-22 stage 3: the post-quantum validator identity, in ONE place. + * + * WHAT IT DOES. From the activation height H_id, a validator is its 20-byte id (PqRegistryV3), not its + * ECDSA address. Upstream QBFT works entirely on two 20-byte things - the AUTHOR of a signed message and + * the validator LIST - so the switch is a TRANSLATION applied at the few places where an ECDSA address + * is born, and nowhere else (the choke points are listed in the AIP-22 plan, section 9.4): + * + * author(recovered, h) the author of a message or commit seal at height h: the id of the v3 row + * whose ecdsa.addr is the recovered address when h >= H_id, the address itself + * otherwise (an address with no row is then simply not in the set, and its + * message does not count - the upstream checks already say so) + * translateSet(set, b) the validator set after block b = H_id - 1 becomes the sorted ids of its + * members (VoteTallyUpdater), so extraData(H_id) carries ids and H_id is the + * first block sealed by ids; a member without a v3 row is a configuration + * FAULT that throws, never a silent drop + * local(address, h) this node's own identity at height h (coinbase, proposer check) + * isLocal(candidate, a) whether a 20-byte value is this node, in either form + * transportTargets(ids) the RLPx node-key addresses to send consensus messages to, from the rows' + * transport field; empty when some id lists none (send to every peer then) + * + * DISARMED unless installed: with no configuration every function is the identity, so a build that + * carries this class and is never configured behaves exactly as before, byte for byte. + * + * Configuration (system properties, like every AERE rule, read once at startup): + * aere.pq.identityBlock H_id + * aere.pq.registryV3 comma list of v3 manifests (JSON), each bound at its own bindHeight + * aere.pq.registryV3.hashes comma list "bindHeight:0xhash" - the pinned hash of every epoch; an + * epoch without a pin refuses startup (a registry nobody pinned is a file) + * + * NOT here (stated): the PQ seal registry lookups (index -> address) move to ids through the v3 epoch + * entering the registry schedule; the per-message PQ seal authorship check (SPEC 2.6 rule 2) then + * compares id with id. + */ +package org.hyperledger.besu.consensus.common.bft; + +import org.hyperledger.besu.datatypes.Address; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Splitter; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** The AIP-22 identity translation. */ +public final class AerePqIdentity { + + private static final Logger LOG = LoggerFactory.getLogger(AerePqIdentity.class); + + /** H_id. */ + public static final String PROPERTY_BLOCK = "aere.pq.identityBlock"; + + /** The v3 manifests. */ + public static final String PROPERTY_REGISTRIES = "aere.pq.registryV3"; + + /** The pinned hashes of the v3 manifests. */ + public static final String PROPERTY_HASHES = "aere.pq.registryV3.hashes"; + + /** + * AIP-22 stage 3b: H_k, the height from which the keys that SEAL come from the v3 epoch in force + * (instead of the Falcon registry schedule and the hybrid registry). Must be the bindHeight of a v3 + * epoch and at or above H_id. Unset: the keys stay where they are (stage 3a). + */ + public static final String PROPERTY_KEYS_FROM = "aere.pq.registryV3.keysFrom"; + + /** No H_k: the v3 epochs never supply keys. */ + public static final long NEVER = Long.MAX_VALUE; + + /** + * An installed configuration. + * + * @param identityBlock H_id + * @param epochs v3 epochs by bindHeight + * @param keysFrom H_k, or {@link #NEVER} + */ + public record Config( + long identityBlock, NavigableMap epochs, long keysFrom) { + + /** + * A stage-3a configuration: identity only, keys stay in the existing registries. + * + * @param identityBlock H_id + * @param epochs v3 epochs by bindHeight + */ + public Config(final long identityBlock, final NavigableMap epochs) { + this(identityBlock, epochs, NEVER); + } + } + + /** A configuration fault of the identity switch. */ + public static final class IdentityException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * A fault. + * + * @param message what is wrong + */ + public IdentityException(final String message) { + super("AERE-PQC-ID: " + message); + } + } + + private static volatile Config config; + + private AerePqIdentity() {} + + /** + * Installs a configuration (startup, or a test). Checks that H_id has an epoch bound at or below it. + * + * @param c the configuration, or null to disarm + */ + public static void install(final Config c) { + if (c != null && c.epochs().floorEntry(c.identityBlock()) == null) { + throw new IdentityException( + "no v3 registry epoch is bound at or below the identity height " + c.identityBlock()); + } + if (c != null && c.keysFrom() != NEVER) { + // H_k switches where the keys come FROM; it must fall on an epoch boundary at or after H_id, so + // the epoch that starts supplying keys is a whole epoch, verified and pinned like every other + if (c.keysFrom() < c.identityBlock() || !c.epochs().containsKey(c.keysFrom())) { + throw new IdentityException( + PROPERTY_KEYS_FROM + + "=" + + c.keysFrom() + + " must be the bindHeight of a v3 epoch at or above the identity height " + + c.identityBlock() + + " (epochs " + + c.epochs().keySet() + + ")"); + } + } + config = c; + if (c != null) { + LOG.info( + "AERE PQ IDENTITY: ARMED - validators are post-quantum ids from height {}; v3 epochs {}{}", + c.identityBlock(), + c.epochs().keySet(), + c.keysFrom() == NEVER + ? "" + : "; the keys that seal come from the v3 epoch in force from height " + c.keysFrom()); + } + } + + /** Disarms (tests). */ + public static void uninstall() { + config = null; + } + + /** + * The installed configuration. + * + * @return it, if armed + */ + public static Optional installed() { + return Optional.ofNullable(config); + } + + /** + * Reads the configuration from the system properties and installs it. No property: stays disarmed. + * + * @param chainId the chain + * @param requiredAt the schemes every row must carry, as the scheme schedule names them at a height + */ + public static void installFromSystemProperties( + final long chainId, final java.util.function.LongFunction> requiredAt) { + final String block = System.getProperty(PROPERTY_BLOCK); + if (block == null || block.isBlank()) { + return; + } + final long h = Long.parseLong(block.trim()); + final List requiredSchemes = requiredAt.apply(h); + final String files = System.getProperty(PROPERTY_REGISTRIES, ""); + final String hashes = System.getProperty(PROPERTY_HASHES, ""); + final TreeMap pins = new TreeMap<>(); + for (final String p : Splitter.on(',').omitEmptyStrings().trimResults().split(hashes)) { + final int i = p.indexOf(':'); + if (i < 0) { + throw new IdentityException(PROPERTY_HASHES + " entry '" + p + "' is not bindHeight:0xhash"); + } + pins.put(Long.parseLong(p.substring(0, i)), Bytes32.fromHexString(p.substring(i + 1))); + } + final ObjectMapper m = new ObjectMapper(); + final List loaded = new ArrayList<>(); + PqRegistryV3.Registry previous = null; + final List paths = new ArrayList<>(); + for (final String f : Splitter.on(',').omitEmptyStrings().trimResults().split(files)) { + paths.add(Path.of(f)); + } + // epochs are loaded in bindHeight order, each against the one before it (rotations) + final TreeMap byHeight = new TreeMap<>(); + for (final Path p : paths) { + try { + final com.fasterxml.jackson.databind.JsonNode j = m.readTree(Files.readString(p)); + byHeight.put(j.path("bindHeight").asLong(-1), j); + } catch (final IOException e) { + throw new IdentityException("cannot read v3 manifest " + p + ": " + e.getMessage()); + } + } + for (final var e : byHeight.entrySet()) { + final Bytes32 pin = pins.get(e.getKey()); + if (pin == null) { + throw new IdentityException( + "the v3 epoch bound at " + e.getKey() + " has no pinned hash in " + PROPERTY_HASHES); + } + final boolean switchEpoch = previous == null; + final PqRegistryV3.Registry r = + PqRegistryV3.loadPinned(e.getValue(), chainId, requiredSchemes, previous, switchEpoch, pin); + loaded.add(r); + previous = r; + } + final TreeMap epochs = new TreeMap<>(); + for (final PqRegistryV3.Registry r : loaded) { + epochs.put(r.bindHeight(), r); + } + final String keysFrom = System.getProperty(PROPERTY_KEYS_FROM); + install( + new Config( + h, + Collections.unmodifiableNavigableMap(epochs), + keysFrom == null || keysFrom.isBlank() ? NEVER : Long.parseLong(keysFrom.trim()))); + } + + // --------------------------------------------------------------------------------------------- + // AIP-22 stage 3b: the keys that seal, from the v3 epoch in force + + /** + * Whether the keys that seal at this height come from the v3 epoch in force (at or above H_k). + * Every key lookup asks this first; below H_k (and disarmed) it answers false and the caller uses + * its existing registry, untouched. + * + * @param height the height the key is asked for, in the caller's own convention (the parent of an + * anchor for a certificate seal) + * @return true from H_k when armed + */ + public static boolean keysFromV3(final long height) { + final Config c = config; + return c != null && height >= c.keysFrom(); + } + + /** + * The public key of a registry index for a scheme at a height, from the v3 epoch in force, in the + * form the existing registries carry it: Falcon-512 WITHOUT the 0x09 header byte (the 896-byte h + * that {@code FalconPublicKeyParameters} takes), SLH-DSA-SHA2-128s as its 32 bytes. Empty when the + * epoch has no such row or key: the caller then has NO key (fail closed), it never falls back to + * the old registries, because above H_k they are no longer the key set the chain requires. + * + * @param height the height (see {@link #keysFromV3}) + * @param index the registry index carried by the seal + * @param schemeId e.g. "falcon-512" + * @return the key, or empty + */ + public static Optional v3KeyAt(final long height, final int index, final String schemeId) { + return epochAt(height) + .filter(r -> index >= 0 && index < r.rows().size()) + .map(r -> r.rows().get(index)) + .flatMap(row -> row.key(schemeId)) + .map( + k -> + "falcon-512".equals(schemeId) + ? java.util.Arrays.copyOfRange(k.publicKey(), 1, k.publicKey().length) + : k.publicKey().clone()); + } + + /** + * AIP-22 stage 3b-ii: the index of the row of the v3 epoch in force that carries a given public key + * for a scheme (the key in the registry form of {@link #v3KeyAt}). How a node finds, per epoch, + * which row is its own: indices move between epochs, so a key file's index cannot say it. + * + * @param height the height + * @param schemeId e.g. "falcon-512" + * @param publicKey the key, registry form + * @return the row index, or empty when no row of that epoch carries the key + */ + public static Optional v3IndexOfKey( + final long height, final String schemeId, final byte[] publicKey) { + final Optional r = epochAt(height); + if (r.isEmpty() || publicKey == null) { + return Optional.empty(); + } + for (final PqRegistryV3.Row row : r.get().rows()) { + final Optional k = v3KeyAt(height, row.index(), schemeId); + if (k.isPresent() && java.util.Arrays.equals(k.get(), publicKey)) { + return Optional.of(row.index()); + } + } + return Optional.empty(); + } + + /** + * The id of a registry index at a height, from the v3 epoch in force (stage 3b: the index names + * the v3 row directly, from H_k). + * + * @param height the height + * @param index the registry index + * @return the 20-byte id as an address, or empty when the epoch has no such row + */ + public static Optional

v3IdAt(final long height, final int index) { + return epochAt(height) + .filter(r -> index >= 0 && index < r.rows().size()) + .map(r -> Address.wrap(Bytes.wrap(r.rows().get(index).id()))); + } + + /** + * THE CONTINUITY GUARD, run once at startup when H_k is configured: switching the SOURCE of the + * keys at H_k must not change any key. Every key of every row of the epoch in force at H_k must be + * exactly the key the existing registries give that index just below H_k; only a LATER epoch may + * rotate, and only through the rotation signatures of the old keys, which the v3 loader verifies. + * A mismatch, or a key the existing registries cannot produce, refuses startup (fail closed): a + * node that swapped keys at H_k would accept seals no other node accepts. + * + * @param keysBefore (index, schemeId) to the key the existing registries give at H_k - 1, in the + * registry form (see {@link #v3KeyAt}) + */ + public static void verifyKeyContinuity( + final java.util.function.BiFunction> keysBefore) { + final Config c = config; + if (c == null || c.keysFrom() == NEVER) { + return; + } + final PqRegistryV3.Registry r = c.epochs().get(c.keysFrom()); + int checked = 0; + for (final PqRegistryV3.Row row : r.rows()) { + for (final PqRegistryV3.Key k : row.keys().values()) { + final String scheme = k.scheme().id(); + final byte[] v3 = v3KeyAt(c.keysFrom(), row.index(), scheme).orElseThrow(); + final Optional before = keysBefore.apply(row.index(), scheme); + if (before.isEmpty() || !java.util.Arrays.equals(before.get(), v3)) { + throw new IdentityException( + "continuity at " + + PROPERTY_KEYS_FROM + + "=" + + c.keysFrom() + + ": row " + + row.index() + + " " + + scheme + + (before.isEmpty() + ? " has no key in the existing registries at " + (c.keysFrom() - 1) + : " differs from the key the existing registries give at " + (c.keysFrom() - 1)) + + "; switching the key source must not change a key (a rotation belongs in a later" + + " epoch, signed by the old keys)"); + } + checked++; + } + } + LOG.info( + "AERE PQ IDENTITY: key continuity at {} verified, {} keys of {} rows equal the existing" + + " registries at {}", + c.keysFrom(), + checked, + r.rows().size(), + c.keysFrom() - 1); + } + + // --------------------------------------------------------------------------------------------- + + /** + * Whether ids are the identity at this height. + * + * @param height the height + * @return true from H_id when armed + */ + public static boolean active(final long height) { + final Config c = config; + return c != null && height >= c.identityBlock(); + } + + /** + * The v3 epoch in force at a height. + * + * @param height the height + * @return the epoch, if armed and one is bound at or below it + */ + public static Optional epochAt(final long height) { + final Config c = config; + if (c == null) { + return Optional.empty(); + } + final var e = c.epochs().floorEntry(height); + return e == null ? Optional.empty() : Optional.of(e.getValue()); + } + + /** + * The author of a message or commit seal whose ECDSA signature recovered to this address. + * + * @param recovered the recovered address + * @param height the height the message or seal is for + * @return the id from H_id (when a row links the address), the address otherwise + */ + public static Address author(final Address recovered, final long height) { + if (!active(height)) { + return recovered; + } + return idOfEcdsa(recovered.getBytes().toArrayUnsafe(), height) + .map(i -> Address.wrap(Bytes.wrap(i))) + .orElse(recovered); + } + + /** + * The id an ECDSA address links to at a height. The ecdsa link is required only in the switch epoch + * (AIP-22 3.2) and a later epoch may omit it, while messages still travel with an ECDSA signature + * until stage 4; so the link is INHERITED by id: the most recent epoch at or below the height that + * links this address names the id, and that id must still be registered in the epoch in force (a + * removed id's old link means nothing). A new id that never carried a link has no ECDSA author, so + * its classical messages cannot count before stage 4: stated in the plan (9.5), not hidden. + */ + private static Optional idOfEcdsa(final byte[] address, final long height) { + final Config c = config; + if (c == null) { + return Optional.empty(); + } + final var head = c.epochs().floorEntry(height); + if (head == null) { + return Optional.empty(); + } + // ids that a NEWER epoch already links to some address: an older link of another address to such an + // id is superseded (the id moved its ECDSA key), so that older address no longer authors as the id + final java.util.Set relinked = new java.util.HashSet<>(); + for (final PqRegistryV3.Registry e : c.epochs().headMap(height, true).descendingMap().values()) { + final Optional linked = e.rowOfEcdsa(address); + if (linked.isPresent()) { + final byte[] i = linked.get().id(); + return !relinked.contains(Bytes.wrap(i)) && head.getValue().rowOf(i).isPresent() + ? Optional.of(i) + : Optional.empty(); + } + for (final PqRegistryV3.Row r : e.rows()) { + if (r.ecdsaAddress() != null) { + relinked.add(Bytes.wrap(r.id())); + } + } + } + return Optional.empty(); + } + + /** + * This node's identity at a height. + * + * @param localEcdsa the node's ECDSA address + * @param height the height + * @return the id from H_id when the node has a row, the address otherwise + */ + public static Address local(final Address localEcdsa, final long height) { + return author(localEcdsa, height); + } + + /** + * Whether a 20-byte value is this node, as an address or as its id in any epoch. + * + * @param candidate the value + * @param localEcdsa the node's ECDSA address + * @return whether it is this node + */ + public static boolean isLocal(final Address candidate, final Address localEcdsa) { + if (candidate.equals(localEcdsa)) { + return true; + } + final Config c = config; + if (c == null) { + return false; + } + for (final PqRegistryV3.Registry r : c.epochs().values()) { + final Optional row = r.rowOfEcdsa(localEcdsa.getBytes().toArrayUnsafe()); + if (row.isPresent() && candidate.equals(Address.wrap(Bytes.wrap(row.get().id())))) { + return true; + } + } + return false; + } + + /** + * Whether the set after this block changes form (armed, and the block is H_id - 1). Callers ask + * first so that, disarmed or at any other block, the upstream code path is untouched - not even an + * extra call on the tally (upstream VoteTallyUpdaterTest asserts exactly that). + * + * @param blockNumber the block + * @return true only at H_id - 1 when armed + */ + public static boolean translatesAfter(final long blockNumber) { + final Config c = config; + return c != null && blockNumber == c.identityBlock() - 1L; + } + + /** + * The validator set after block {@code blockNumber}, translated when that block is H_id - 1. + * + * @param set the set after the block, as upstream computed it + * @param blockNumber the block + * @return the same set, or its sorted ids at H_id - 1 + */ + public static Collection
translateSetAfter( + final Collection
set, final long blockNumber) { + final Config c = config; + if (c == null || blockNumber != c.identityBlock() - 1L) { + return set; + } + final PqRegistryV3.Registry r = + epochAt(c.identityBlock()) + .orElseThrow(() -> new IdentityException("no v3 epoch at " + c.identityBlock())); + final TreeSet
ids = new TreeSet<>(); + for (final Address a : set) { + final Optional row = r.rowOfEcdsa(a.getBytes().toArrayUnsafe()); + if (row.isEmpty()) { + throw new IdentityException( + "validator " + + a + + " is in the set after block " + + blockNumber + + " and has no row in the v3 epoch bound at " + + r.bindHeight() + + "; the identity switch refuses rather than dropping a validator"); + } + ids.add(Address.wrap(Bytes.wrap(row.get().id()))); + } + LOG.info( + "AERE PQ IDENTITY: the set after block {} is translated to {} post-quantum ids", + blockNumber, + ids.size()); + return ids; + } + + /** + * The RLPx node-key addresses consensus messages for these recipients go to. + * + * @param recipients validator identities (ids or addresses) + * @return the transport addresses; EMPTY when some id lists no transport, meaning "every peer" + */ + public static Optional> transportTargets(final Collection
recipients) { + final Config c = config; + if (c == null) { + return Optional.of(recipients); + } + final PqRegistryV3.Registry head = c.epochs().lastEntry().getValue(); + final List
out = new ArrayList<>(); + for (final Address a : recipients) { + final Optional row = head.rowOf(a.getBytes().toArrayUnsafe()); + if (row.isEmpty()) { + out.add(a); // a classical address (below H_id), sent to as before + } else if (row.get().transport() == null) { + return Optional.empty(); + } else { + out.add(Address.wrap(Bytes.wrap(row.get().transport()))); + } + } + return Optional.of(out); + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockHashing.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockHashing.java index 94dc41b354..06cb610099 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockHashing.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockHashing.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.common.bft; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.tuweni.bytes.Bytes; /** The Bft block hashing. */ public class BftBlockHashing { private final BftExtraDataCodec bftExtraDataCodec; @@ -85,41 +91,41 @@ public class BftBlockHashing { final BftExtraData bftExtraData = bftExtraDataCodec.decode(header); return Hash.hash( serializeHeader( header, () -> bftExtraDataCodec.encodeWithoutCommitSealsAndRoundNumber(bftExtraData), bftExtraDataCodec)); } /** * Recovers the {@link Address} for each validator that contributed a committed seal to the block. * * @param header the block header that was signed by the committed seals * @param bftExtraData the parsed {@link BftExtraData} from the header * @return the addresses of validators that provided a committed seal */ public List
recoverCommitterAddresses( final BlockHeader header, final BftExtraData bftExtraData) { final Hash committerHash = calculateDataHashForCommittedSeal(header, bftExtraData); return bftExtraData.getSeals().stream() - .map(p -> Util.signatureToAddress(p, committerHash)) + .map(p -> AerePqIdentity.author(Util.signatureToAddress(p, committerHash), header.getNumber())) .collect(Collectors.toList()); } /** * Serialize header bytes. * * @param header the header * @param extraDataSerializer the extra data serializer * @param bftExtraDataCodec the bft extra data codec * @return the bytes */ public static Bytes serializeHeader( final BlockHeader header, final Supplier extraDataSerializer, final BftExtraDataCodec bftExtraDataCodec) { // create a block header which is a copy of the header supplied as parameter except of the // extraData field final BlockHeaderBuilder builder = BlockHeaderBuilder.fromHeader(header); builder.blockHeaderFunctions(BftBlockHeaderFunctions.forOnchainBlock(bftExtraDataCodec)); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java index 77af53d688..acefe61665 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftBlockInterface.java @@ -100,63 +100,66 @@ public class BftBlockInterface implements BlockInterface { * * @param block the block * @param round the round * @param blockHeaderFunctions the block header functions * @return the block */ public Block replaceRoundInBlock( final Block block, final int round, final BlockHeaderFunctions blockHeaderFunctions) { final BftExtraData prevExtraData = bftExtraDataCodec.decode(block.getHeader()); final BftExtraData substituteExtraData = new BftExtraData( prevExtraData.getVanityData(), prevExtraData.getSeals(), prevExtraData.getVote(), round, prevExtraData.getValidators(), prevExtraData.getFalconSeals(), // 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 // 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()); headerBuilder .extraData(bftExtraDataCodec.encode(substituteExtraData)) .blockHeaderFunctions(blockHeaderFunctions); final BlockHeader newHeader = headerBuilder.buildBlockHeader(); return new Block(newHeader, block.getBody()); } /** * Gets extra data. * * @param header the header * @return the extra data */ public BftExtraData getExtraData(final BlockHeader header) { return bftExtraDataCodec.decode(header); } /** * Gets committers. * * @param header the header * @return the committers */ public List
getCommitters(final BlockHeader header) { final BftExtraData bftExtraData = bftExtraDataCodec.decode(header); final Hash committerHash = Hash.hash( BftBlockHashing.serializeHeader( header, () -> bftExtraDataCodec.encodeWithoutCommitSeals(bftExtraData), bftExtraDataCodec)); return bftExtraData.getSeals().stream() - .map(p -> Util.signatureToAddress(p, committerHash)) + .map(p -> AerePqIdentity.author(Util.signatureToAddress(p, committerHash), header.getNumber())) .collect(Collectors.toList()); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java index c444a1514d..4e9ea6f783 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftExtraData.java @@ -32,40 +32,46 @@ import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; /** The Bft extra data. */ public class BftExtraData implements ParsedExtraData { private final Bytes vanityData; private final Collection seals; private final Collection
validators; private final Optional vote; private final int round; /** * Parallel, ADDITIVE post-quantum (Falcon-512) seals. These sit alongside the decisive ECDSA * {@link #seals} and are excluded from every signed pre-image, so they never disturb the ECDSA * committed seals or the block/header hash. */ private final Collection falconSeals; /** AERE ANCHOR V2: the scheme-tagged certificate; empty on every header below the v2 height. */ private final List 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 proposerSeal; /** * Instantiates a new Bft extra data (no Falcon seals). Retained for callers, notably the IBFT * codec, that never carry a parallel post-quantum seal. * * @param vanityData the vanity data * @param seals the seals * @param vote the vote * @param round the round * @param validators the validators */ public BftExtraData( final Bytes vanityData, final Collection seals, final Optional vote, final int round, final Collection
validators) { this(vanityData, seals, vote, round, validators, Collections.emptyList()); } @@ -86,41 +92,67 @@ public class BftExtraData implements ParsedExtraData { final int round, final Collection
validators, final Collection falconSeals) { this(vanityData, seals, vote, round, validators, falconSeals, Collections.emptyList()); } /** * Full constructor. * * @param hybridSeals the scheme-tagged (v2) certificate, or empty; a header carries the v1 Falcon * list OR the v2 certificate, never both */ public BftExtraData( final Bytes vanityData, final Collection seals, final Optional vote, final int round, final Collection
validators, final Collection falconSeals, final List 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 seals, + final Optional vote, + final int round, + final Collection
validators, + final Collection falconSeals, + final List hybridSeals, + final Optional proposerSeal) { checkNotNull(vanityData); + checkNotNull(proposerSeal); + this.proposerSeal = proposerSeal; checkNotNull(hybridSeals); if (!hybridSeals.isEmpty() && falconSeals != null && !falconSeals.isEmpty()) { throw new IllegalArgumentException( "AERE ANCHOR V2: a header carries the v1 Falcon list OR the v2 certificate, not both"); } this.hybridSeals = List.copyOf(hybridSeals); checkNotNull(seals); checkNotNull(validators); checkNotNull(falconSeals); this.vanityData = vanityData; this.seals = seals; this.validators = validators; this.vote = vote; this.round = round; this.falconSeals = falconSeals; } /** * Gets vanity data. * @@ -167,40 +199,66 @@ public class BftExtraData implements ParsedExtraData { } /** * Gets the parallel post-quantum Falcon seals. * * @return the Falcon seals (possibly empty, never null) */ public Collection getFalconSeals() { return falconSeals; } /** * The scheme-tagged (v2) certificate. * * @return the v2 seals, canonical order; empty below the v2 height */ public List getHybridSeals() { return hybridSeals; } + /** + * The proposer's post-quantum seal (AIP-22 stage 5, element 6). + * + * @return the seal, or empty below H_hdr + */ + public Optional 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 newSeals, final Optional newProposerSeal) { + return new BftExtraData( + vanityData, newSeals, vote, round, validators, falconSeals, hybridSeals, newProposerSeal); + } + @Override public String toString() { return "BftExtraData{" + "vanityData=" + vanityData + ", seals=" + seals + ", validators=" + validators + ", vote=" + vote + ", round=" + round + ", falconSeals=" + falconSeals + ", hybridSeals=" + hybridSeals + + ", proposerSeal=" + + proposerSeal + '}'; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealSupport.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealSupport.java index 7049b84147..8482c3d47e 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealSupport.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/FalconSealSupport.java @@ -134,40 +134,61 @@ public final class FalconSealSupport { /** Storage slot (0) at the anchor address holding the manifest hash. 64-hex, no 0x prefix. */ private static final String ANCHOR_SLOT = "0000000000000000000000000000000000000000000000000000000000000000"; /** How many seals this node emitted on its own PREPAREs. See {@link #preparesSealed()}. */ private final java.util.concurrent.atomic.AtomicLong preparesSealed = new java.util.concurrent.atomic.AtomicLong(); /** How many seals this node emitted on its own PROPOSALs since startup. */ private final java.util.concurrent.atomic.AtomicLong proposalsSealed = new java.util.concurrent.atomic.AtomicLong(); /** How many seals this node emitted on its own ROUND-CHANGEs since startup. */ private final java.util.concurrent.atomic.AtomicLong roundChangesSealed = new java.util.concurrent.atomic.AtomicLong(); private final boolean signingEnabled; private final int localIndex; private final FalconPrivateKeyParameters localPrivateKey; + + /** + * AERE AIP-22 stage 3b-ii: this node's NEXT Falcon keys ({@code aere.falcon.keys.next} / + * {@code AERE_FALCON_KEYS_NEXT}, comma list of key files in the same format as {@code + * aere.falcon.key}), for the v3 epochs that rotate its key. From H_k the key and the index this node + * signs with at a height are the ones of the v3 row that carries one of its keys (see {@link + * #localAt}); the file's "index" property then means nothing, because indices move between epochs. + */ + private final java.util.List nextPrivateKeys; + + /** The local key and index resolved per v3 epoch (bindHeight), from H_k. */ + private final Map> localByEpoch = new ConcurrentHashMap<>(); + + /** + * A local signing key with the registry index it signs as. + * + * @param key the private key + * @param index the registry index at the height it was resolved for + */ + private record LocalKey(FalconPrivateKeyParameters key, int index) {} + private final Map registry; /** * Validator ADDRESS bound to each registry index (AUD-CONSENSUS-1 fix). Populated in lockstep * with {@link #registry} from an address-bound manifest; empty for the legacy pubkey-only source. * This is the map that makes {@code currentValidators INTERSECT registry} computable. */ private final Map indexToAddress; private final boolean genesisAnchored; /** * AERE A8: the registry file this node ACTUALLY used, and which of the three sources it came from. * Recorded so the consensus-binding guard can re-read exactly that file and hash it, rather than * hashing something adjacent to it. Null when no registry is configured at all. */ private final String registrySourcePath; private final PqRegistryHash.SourceKind registrySourceKind; @@ -573,40 +594,70 @@ public final class FalconSealSupport { try (InputStream in = new FileInputStream(keyPath)) { final Properties p = new Properties(); p.load(in); idx = Integer.parseInt(p.getProperty("index").trim()); final byte[] f = Bytes.fromHexStringLenient(p.getProperty("f").trim()).toArray(); final byte[] g = Bytes.fromHexStringLenient(p.getProperty("g").trim()).toArray(); final byte[] bigF = Bytes.fromHexStringLenient(p.getProperty("F").trim()).toArray(); final byte[] pk = Bytes.fromHexStringLenient(p.getProperty("pk").trim()).toArray(); priv = new FalconPrivateKeyParameters(FalconParameters.falcon_512, f, g, bigF, pk); LOG.info( "AERE PQC: loaded local Falcon signing key for validator index {} from {}", idx, keyPath); } catch (final Exception e) { LOG.warn("AERE PQC: failed to load Falcon signing key from {}: {}", keyPath, e.toString()); idx = -1; priv = null; } } + // AERE AIP-22 stage 3b-ii: the next keys, same file format; a file that does not load refuses + // startup, because a validator that silently lacks its rotated key goes mute at the rotation epoch + final java.util.List next = new java.util.ArrayList<>(); + final String nextPaths = resolve("aere.falcon.keys.next", "AERE_FALCON_KEYS_NEXT"); + if (nextPaths != null) { + for (final String np : com.google.common.base.Splitter.on(',').omitEmptyStrings().trimResults().split(nextPaths)) { + if (np.isBlank()) { + continue; + } + try (InputStream in = new FileInputStream(np.trim())) { + final Properties p = new Properties(); + p.load(in); + next.add( + new FalconPrivateKeyParameters( + FalconParameters.falcon_512, + Bytes.fromHexStringLenient(p.getProperty("f").trim()).toArray(), + Bytes.fromHexStringLenient(p.getProperty("g").trim()).toArray(), + Bytes.fromHexStringLenient(p.getProperty("F").trim()).toArray(), + Bytes.fromHexStringLenient(p.getProperty("pk").trim()).toArray())); + LOG.info("AERE PQC: loaded a NEXT Falcon signing key from {} (AIP-22 stage 3b-ii)", np.trim()); + } catch (final Exception e) { + throw new IllegalStateException( + "AERE-PQC-KEY-NEXT-01: cannot load the next Falcon key from '" + + np.trim() + + "': " + + e.getMessage()); + } + } + } + this.nextPrivateKeys = java.util.List.copyOf(next); this.registry = reg; this.indexToAddress = regAddr; this.localIndex = idx; this.localPrivateKey = priv; this.signingEnabled = priv != null && idx >= 0; this.manifestHash = mHash; this.genesisAnchored = anchored; this.pendingLate = late; this.pendingLateAddresses = lateAddr; this.pendingLateHash = lateHash; this.anchorContractAddress = anchorAddr; // AERE A8: remember WHICH file the registry came from, so the consensus-binding guard hashes // the file this node is really running on and not a different one that happens to be nearby. if (genesisPath != null) { this.registrySourcePath = genesisPath; this.registrySourceKind = PqRegistryHash.SourceKind.GENESIS_MANIFEST; } else if (manifestPath != null && anchorProp != null) { this.registrySourcePath = manifestPath; this.registrySourceKind = PqRegistryHash.SourceKind.MANIFEST_JSON; @@ -2507,40 +2558,123 @@ public final class FalconSealSupport { "AERE PQC: LATE-ANCHOR ACTIVATED - Falcon registry ({} entries, address-bound={}) is now " + "live; keccak256(manifest)=0x{} MATCHES the on-chain anchor at contract {} slot 0 " + "(readable via eth_getStorageAt). Hybrid PQC certificate can now be verified on an " + "already-live chain with NO re-genesis.", registry.size(), addressBound(), pendingLateHash, anchorContractAddress); return true; } /** * Whether this node holds a Falcon signing key and should attach a parallel seal. * * @return true if signing is enabled */ public boolean signingEnabled() { return signingEnabled; } + /** + * AERE AIP-22 stage 3b-ii. The key this node signs with at a height, and the index it signs as. + * Below H_k (and disarmed): the key of aere.falcon.key and its file index, exactly as before. From + * H_k: the v3 row of the epoch in force whose Falcon key is the public key of one of this node's + * keys (the base key or a next key); indices move between epochs, so the index is that row's. No + * row carries any of this node's keys: null, so nothing is signed (the startup guard {@link + * #verifyLocalKeysCoverV3EpochsOrAbort} refuses a validator that would end up there). + */ + private LocalKey localAt(final long height) { + if (!AerePqIdentity.keysFromV3(height)) { + return localPrivateKey == null ? null : new LocalKey(localPrivateKey, localIndex); + } + final long bind = + AerePqIdentity.epochAt(height).map(PqRegistryV3.Registry::bindHeight).orElse(-1L); + return localByEpoch + .computeIfAbsent( + bind, + b -> { + final java.util.List mine = new java.util.ArrayList<>(); + if (localPrivateKey != null) { + mine.add(localPrivateKey); + } + mine.addAll(nextPrivateKeys); + for (final FalconPrivateKeyParameters k : mine) { + final java.util.Optional i = + AerePqIdentity.v3IndexOfKey(height, "falcon-512", k.getPublicKey()); + if (i.isPresent()) { + return java.util.Optional.of(new LocalKey(k, i.get())); + } + } + return java.util.Optional.empty(); + }) + .orElse(null); + } + + /** + * AERE AIP-22 stage 3b-ii. The registry index this node signs as at a height (the index of every + * other scheme's seal of this node too: an SLH-DSA seal must sit at the index of its Falcon seal). + * + * @param height the height + * @return the index, or -1 when this node holds no key of that height + */ + public int localIndexAt(final long height) { + final LocalKey lk = localAt(height); + return lk == null ? -1 : lk.index(); + } + + /** + * AERE AIP-22 stage 3b-ii, THE STARTUP GUARD of the local keys: in every v3 epoch from H_k in which + * this node's identity is registered, this node must hold the Falcon key of its row, otherwise it + * refuses to start. A validator whose rotated key is missing would sign nothing from the rotation + * epoch on: mute, while every gate that asks "is it configured?" stays green (D-307). + * + * @param thisNodeAddress this node's own validator (ECDSA) address + */ + public void verifyLocalKeysCoverV3EpochsOrAbort(final Address thisNodeAddress) { + final java.util.Optional c = AerePqIdentity.installed(); + if (c.isEmpty() || c.get().keysFrom() == AerePqIdentity.NEVER || thisNodeAddress == null) { + return; + } + for (final PqRegistryV3.Registry e : c.get().epochs().tailMap(c.get().keysFrom(), true).values()) { + final Address me = AerePqIdentity.author(thisNodeAddress, e.bindHeight()); + if (e.rowOf(me.getBytes().toArrayUnsafe()).isEmpty()) { + continue; // this node is not a registered validator in this epoch: nothing to sign + } + if (localAt(e.bindHeight()) == null) { + throw new IllegalStateException( + "AERE-PQC-KEY-NEXT-02: REFUSING TO START. This node (" + + thisNodeAddress + + ", id " + + me + + ") is registered in the v3 epoch bound at " + + e.bindHeight() + + " but holds no Falcon key of its row there (aere.falcon.key / aere.falcon.keys.next);" + + " from that height it would sign nothing."); + } + LOG.info( + "AERE PQC: local Falcon key covers the v3 epoch bound at {} (index {}).", + e.bindHeight(), + localAt(e.bindHeight()).index()); + } + } + /** * This node's validator index in the Falcon registry. * * @return the local validator index, or -1 if signing is disabled */ public int localIndex() { return localIndex; } /** * Number of validators in the Falcon registry. * * @return the registry size */ public int registrySize() { return registry.size(); } // ---- AERE audit fix (AUD-CONSENSUS-1 / -2): address binding + eligible-signer set ---- @@ -3443,47 +3577,51 @@ public final class FalconSealSupport { * Falcon-sign a commit hash with this node's Falcon key, producing a parallel seal. Never throws. * *

AERE FIX-OPRIRE-CONSENS (b): the block number is MANDATORY and the attachment gate is * checked here, at the single point where a seal can be produced. There is deliberately no * height-less overload, so no call site can attach a seal without passing the gate. * * @param blockNumber the number of the block whose commit is being sealed * @param commitHash the same 32-byte commit hash the ECDSA committed seal signs * @return the Falcon seal, or empty if signing is disabled, gated off, or failed */ public Optional sign(final long blockNumber, final Bytes32 commitHash) { if (!signingEnabled) { return Optional.empty(); } if (!attachmentArmed(blockNumber)) { return Optional.empty(); } // AERE AGILITY step 4 (2026-08-24): signing goes through the scheme layer. The same // FalconSigner underneath, but the path is now the one the hybrid will use too; the 751 // baseline tests prove the equivalence. + final LocalKey lk = localAt(blockNumber); // AERE AIP-22 stage 3b-ii + if (lk == null) { + return Optional.empty(); + } final java.util.Optional sig = - ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(localPrivateKey, commitHash.toArray()); + ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(lk.key(), commitHash.toArray()); if (sig.isEmpty()) { LOG.warn("AERE PQC: Falcon signing failed (ECDSA seal unaffected)"); return Optional.empty(); } - return Optional.of(new FalconSeal(localIndex, Bytes.wrap(sig.get()))); + return Optional.of(new FalconSeal(lk.index(), Bytes.wrap(sig.get()))); } /** * Property naming the first height at which this node ATTACHES a post-quantum seal to its own * PREPARE messages. Absent = never, which is the configuration of every node today. * *

SEPARATE from {@code aere.falcon.attachBlock}, and the separation is mandatory: if PREPARE * emission started together with commit emission, rolling the binary onto the fleet would become * a flag day. This way the binary can sit on every node for months before any of them emits * anything new. */ public static final String PREPARE_ATTACH_PROPERTY = "aere.pq.preparePq.attachBlock"; /** Environment fallback for {@link #PREPARE_ATTACH_PROPERTY}. */ public static final String PREPARE_ATTACH_ENV = "AERE_PQ_PREPAREPQ_ATTACHBLOCK"; /** * The configured PREPARE attachment height, read fresh on every call. * *

Absent = {@link Long#MAX_VALUE}, i.e. never. A value that is PRESENT but unreadable REFUSES @@ -3531,63 +3669,67 @@ public final class FalconSealSupport { *

  • its own height, above. * * *

    NEVER THROWS except for the strict configuration case above: a signing failure is a log line * and an empty value, exactly as at commit, because the ECDSA path must not be disturbed. * * @param blockNumber the height being prepared * @param message the domain-separated PREPARE message (see PqAnchor.prepareMessage) * @return the seal, or empty when any gate is shut */ public Optional signPrepare(final long blockNumber, final Bytes32 message) { if (!signingEnabled) { return Optional.empty(); } if (!attachmentArmed(blockNumber)) { return Optional.empty(); } if (blockNumber < prepareAttachBlock()) { return Optional.empty(); } + final LocalKey lk = localAt(blockNumber); // AERE AIP-22 stage 3b-ii + if (lk == null) { + return Optional.empty(); + } final java.util.Optional sig = - ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(localPrivateKey, message.toArray()); + ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(lk.key(), message.toArray()); if (sig.isEmpty()) { LOG.warn("AERE PQ PREPARE: Falcon signing failed (ECDSA path unaffected)"); return Optional.empty(); } final long n = preparesSealed.incrementAndGet(); // LOG VOLUME IS A DECISION, not an oversight. One line per PREPARE would be two lines per // second per node, which is exactly the kind of log that gets trained away (see D-153, where a // permanent ERROR made an entire log worthless). The first emission is worth a line, because it // is the moment the node starts doing something new; after that, one line every 500, so a // testnet still has a number to count. if (n == 1L) { LOG.info( "AERE PQ PREPARE: this node EMITTED its first post-quantum seal on a PREPARE, at " + "height {} (gate {}={}). From here on its PREPAREs carry a seal.", blockNumber, PREPARE_ATTACH_PROPERTY, prepareAttachBlock()); } else if (n % 500L == 0L) { LOG.info("AERE PQ PREPARE: {} seals emitted on PREPAREs since startup.", n); } - return Optional.of(new FalconSeal(localIndex, Bytes.wrap(sig.get()))); + return Optional.of(new FalconSeal(lk.index(), Bytes.wrap(sig.get()))); } /** * How many seals this node has emitted on PREPAREs since startup. * *

    It exists so that the COVERAGE step can be measured: without a number, arming enforcement * would be a bet. Read from the log on a testnet, and from this value in tests. * * @return the count */ public long preparesSealed() { return preparesSealed.get(); } /** * Property naming the first height at which this node ATTACHES a post-quantum seal to its own * PROPOSAL messages. Absent = never, which is the configuration of every node today. * *

    SEPARATE from both the commit and the PREPARE gates, same reason: three layers that arm on * three independent decisions must not share a switch, or the day one is turned on becomes a flag @@ -3636,60 +3778,64 @@ public final class FalconSealSupport { * *

    Same three requirements as {@link #signPrepare}, for the same three reasons: a key, the * commit-layer coverage conditions, and its own height. NEVER throws except for the strict * configuration case: a signing failure is a log line and an empty value, because the ECDSA path * must not be disturbed. * * @param blockNumber the height being proposed * @param message the domain-separated PROPOSAL message (see PqAnchor.proposalMessage) * @return the seal, or empty when any gate is shut */ public Optional signProposal(final long blockNumber, final Bytes32 message) { if (!signingEnabled) { return Optional.empty(); } if (!attachmentArmed(blockNumber)) { return Optional.empty(); } if (blockNumber < proposalAttachBlock()) { return Optional.empty(); } + final LocalKey lk = localAt(blockNumber); // AERE AIP-22 stage 3b-ii + if (lk == null) { + return Optional.empty(); + } final java.util.Optional sig = - ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(localPrivateKey, message.toArray()); + ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(lk.key(), message.toArray()); if (sig.isEmpty()) { LOG.warn("AERE PQ PROPOSAL: Falcon signing failed (ECDSA path unaffected)"); return Optional.empty(); } final long n = proposalsSealed.incrementAndGet(); // Same log-volume decision as at PREPARE: a proposal is at most one per round per proposer, so // this is quieter by nature, but the counter still steps so a testnet has a number to count. if (n == 1L) { LOG.info( "AERE PQ PROPOSAL: this node EMITTED its first post-quantum seal on a PROPOSAL, at " + "height {} (gate {}={}). From here on its proposals carry a seal.", blockNumber, PROPOSAL_ATTACH_PROPERTY, proposalAttachBlock()); } else if (n % 500L == 0L) { LOG.info("AERE PQ PROPOSAL: {} seals emitted on PROPOSALs since startup.", n); } - return Optional.of(new FalconSeal(localIndex, Bytes.wrap(sig.get()))); + return Optional.of(new FalconSeal(lk.index(), Bytes.wrap(sig.get()))); } /** * How many seals this node has emitted on its own PROPOSALs since startup. Exists so the * coverage step can be measured, exactly as at PREPARE. * * @return the count */ public long proposalsSealed() { return proposalsSealed.get(); } /** * The property that opens the ROUND-CHANGE attachment gate: from this height on, this node's own * round-changes carry a Falcon seal. Emission only - enforcement is {@code * aere.pq.roundChangePq.forkBlock}, deliberately separate so the fleet can emit long before any * node refuses, exactly as at PREPARE and PROPOSAL. */ public static final String ROUNDCHANGE_ATTACH_PROPERTY = "aere.pq.roundChangePq.attachBlock"; @@ -3735,60 +3881,64 @@ public final class FalconSealSupport { * *

    Same three requirements as {@link #signPrepare} and {@link #signProposal}, for the same * three reasons: a key, the commit-layer coverage conditions, and its own height. NEVER throws * except for the strict configuration case: a signing failure is a log line and an empty value, * because the ECDSA path must not be disturbed. * * @param blockNumber the height the round-change targets * @param message the domain-separated ROUND-CHANGE message (see PqAnchor.roundChangeMessage) * @return the seal, or empty when any gate is shut */ public Optional signRoundChange(final long blockNumber, final Bytes32 message) { if (!signingEnabled) { return Optional.empty(); } if (!attachmentArmed(blockNumber)) { return Optional.empty(); } if (blockNumber < roundChangeAttachBlock()) { return Optional.empty(); } + final LocalKey lk = localAt(blockNumber); // AERE AIP-22 stage 3b-ii + if (lk == null) { + return Optional.empty(); + } final java.util.Optional sig = - ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(localPrivateKey, message.toArray()); + ((FalconSealScheme) SealSchemes.FALCON_512).signWithParams(lk.key(), message.toArray()); if (sig.isEmpty()) { LOG.warn("AERE PQ ROUNDCHANGE: Falcon signing failed (ECDSA path unaffected)"); return Optional.empty(); } final long n = roundChangesSealed.incrementAndGet(); // Round-changes only exist when rounds FAIL, so on a healthy chain this counter barely moves; // it still steps so a testnet that forces round-changes has a number to count. if (n == 1L) { LOG.info( "AERE PQ ROUNDCHANGE: this node EMITTED its first post-quantum seal on a ROUND-CHANGE, " + "at height {} (gate {}={}). From here on its round-changes carry a seal.", blockNumber, ROUNDCHANGE_ATTACH_PROPERTY, roundChangeAttachBlock()); } else if (n % 100L == 0L) { LOG.info("AERE PQ ROUNDCHANGE: {} seals emitted on ROUND-CHANGEs since startup.", n); } - return Optional.of(new FalconSeal(localIndex, Bytes.wrap(sig.get()))); + return Optional.of(new FalconSeal(lk.index(), Bytes.wrap(sig.get()))); } /** * How many seals this node has emitted on its own ROUND-CHANGEs since startup. Exists so the * coverage step can be measured, exactly as at PREPARE and PROPOSAL. * * @return the count */ public long roundChangesSealed() { return roundChangesSealed.get(); } /** * Verify a Falcon seal against the registry public key for its validator index. Never throws. * * @param validatorIndex the signer index * @param commitHash the commit hash that was signed * @param signature the Falcon signature bytes * @return true iff a registry public key exists for the index and the signature verifies */ @@ -3796,40 +3946,49 @@ public final class FalconSealSupport { return verifyWithKey(registry.get(validatorIndex), validatorIndex, commitHash, signature); } /** * D-081. The Falcon public key registered for an index AT A HEIGHT: the one carried by the * registry the chain's schedule makes active there. * *

    WHY A HEIGHT IS NEEDED AT ALL. A certificate inside a block at height h was produced under * the key set the chain required at h. After a rotation the node's own head registry is a * different key set, so checking an old block against it fails for a reason that has nothing to do * with the block. Cosmos ADR-016 states the same requirement for the same reason and keeps a * height-to-key mapping; the only difference here is that ours is never pruned, because this chain * has no unbonding period and a node syncing from genesis verifies every block ever produced. * *

    Returns the head registry's key when no binding is active at that height, which is every * height below the schedule's first entry and every height on a chain with no schedule at all. So * a node on chain 2800 as it stands today gets exactly what it got before D-081. */ private FalconPublicKeyParameters keyAt( final long blockNumber, final int validatorIndex, final boolean historic) { + // AERE AIP-22 stage 3b: from H_k the key of an index is the one of the v3 epoch in force at this + // SAME height (the caller's convention is kept: the parent of an anchor for its certificate). No + // row or no key there means no key: never a fall-back to the registries below, which above H_k + // are no longer the key set the chain requires. Below H_k (and disarmed) nothing changes. + if (AerePqIdentity.keysFromV3(blockNumber)) { + return AerePqIdentity.v3KeyAt(blockNumber, validatorIndex, "falcon-512") + .map(pk -> new FalconPublicKeyParameters(FalconParameters.falcon_512, pk)) + .orElse(null); + } final PqRegistryHash.Schedule schedule = this.registryBindingSchedule; final PqRegistryHash.RegistrySet set = this.registryBindingSet; if (schedule == null || set == null) { return headRegistryKeyOrRefuse( blockNumber, validatorIndex, "no schedule was ever loaded", historic); } final Optional required = PqRegistryHash.requiredHashAt(schedule, blockNumber); if (required.isEmpty()) { // D-228. The only historical question ever asked one block below the schedule's first entry // is about the certificate carried by the block at blockNumber+1, whose governing registry // is the one bound EXACTLY at blockNumber+1. Answer it from that entry's VERIFIED bound // registry, never from the unverified head file. Exactly one block: at blockNumber+2 below // the schedule nothing changes. final Optional nextEntry = PqRegistryHash.requiredHashAt(schedule, blockNumber + 1); if (nextEntry.isPresent() && nextEntry.get().block() == blockNumber + 1) { final Optional boundNext = PqRegistryHash.registryAt(schedule, set, blockNumber + 1); if (boundNext.isPresent()) { @@ -3851,40 +4010,54 @@ public final class FalconSealSupport { } final Optional active = PqRegistryHash.registryAt(schedule, set, blockNumber); if (active.isEmpty()) { // The entry active here is covered by nothing this node holds. Fail closed: no key, so no // seal verifies, and the per-block binding rule has already refused the header for the same // reason. Silence here would be the node deciding a question it cannot answer. return null; } for (final PqRegistryHash.Entry e : active.get().entries()) { if (e.index() == validatorIndex) { final FalconPublicKeyParameters pub = new FalconPublicKeyParameters(FalconParameters.falcon_512, e.publicKey()); historicalKeys.put(cacheKey, pub); return pub; } } return null; } + /** + * AERE AIP-22 stage 3b. The Falcon-512 public key (the 896-byte h, as the registries carry it) that + * the chain's registries give an index at a height, through the same history door as a received + * header. Used by the continuity guard at H_k - 1, where the v3 epochs do not yet supply keys. + * + * @param blockNumber the height + * @param validatorIndex the registry index + * @return the key, or empty when the registries give none there + */ + public Optional publicKeyBytesAtHistoric(final long blockNumber, final int validatorIndex) { + final FalconPublicKeyParameters k = keyAt(blockNumber, validatorIndex, true); + return k == null ? Optional.empty() : Optional.of(k.getH().clone()); + } + /** * D-081. The validator ADDRESS bound to a registry index at a height. * *

    D2 HARDENING (b-v2): PRIVATE, with the caller's motive as an argument. The two public * doors are {@link #addressForIndexAtHistoric} and {@link #addressForIndexAtOwnHead}, each of * which passes a constant. Nothing outside this file can choose the value, so the one shared * resolution body cannot drift between the two paths and no caller can pick the wrong flag. * * @param blockNumber the height of the header being validated * @param validatorIndex the registry index carried by a Falcon seal * @param historic true when the subject is a header this node received, false when it is this * node's own head or the block it is building * @return the bound address, or null when the index is unregistered at that height or the * registry active there is not address-bound */ private Address addressAt( final long blockNumber, final int validatorIndex, final boolean historic) { final PqRegistryHash.Schedule schedule = this.registryBindingSchedule; final PqRegistryHash.RegistrySet set = this.registryBindingSet; if (schedule == null || set == null) { @@ -3983,52 +4156,65 @@ public final class FalconSealSupport { * @param signature the Falcon signature bytes * @return true iff a key exists for the index there and the signature verifies */ public boolean verifyAtOwnHead( final long blockNumber, final int validatorIndex, final Bytes commitHash, final Bytes signature) { return verifyWithKey( keyAt(blockNumber, validatorIndex, false), validatorIndex, commitHash, signature); } /** * D-081 / D2 HARDENING (b-v2). THE HISTORY DOOR, address half. See {@link #verifyAtHistoric}. * * @param blockNumber the height of the header being validated * @param validatorIndex the registry index carried by a Falcon seal * @return the bound address, or null when nothing binds that index at that height */ public Address addressForIndexAtHistoric(final long blockNumber, final int validatorIndex) { - return addressAt(blockNumber, validatorIndex, true); + // AERE AIP-22 stage 3b: from H_k the index names the v3 row directly (its id), like its key + if (AerePqIdentity.keysFromV3(blockNumber)) { + return AerePqIdentity.v3IdAt(blockNumber, validatorIndex).orElse(null); + } + return identityOf(addressAt(blockNumber, validatorIndex, true), blockNumber); } /** * D2 HARDENING (b-v2). THE OWN-HEAD DOOR, address half. See {@link #verifyAtOwnHead}. * * @param blockNumber this node's own head, or the block it is building * @param validatorIndex the registry index * @return the bound address, or null */ public Address addressForIndexAtOwnHead(final long blockNumber, final int validatorIndex) { - return addressAt(blockNumber, validatorIndex, false); + // AERE AIP-22 stage 3b: from H_k the index names the v3 row directly (its id), like its key + if (AerePqIdentity.keysFromV3(blockNumber)) { + return AerePqIdentity.v3IdAt(blockNumber, validatorIndex).orElse(null); + } + return identityOf(addressAt(blockNumber, validatorIndex, false), blockNumber); + } + + /** AERE AIP-22 stage 3: the registry's classical address for an index, as the identity of the height. */ + private static Address identityOf(final Address registryAddress, final long blockNumber) { + return registryAddress == null ? null : AerePqIdentity.author(registryAddress, blockNumber); } /** * D2 HARDENING (b). The one decision the whole hardening turns on, isolated so that removing it is * a one-line edit and the negative control can prove the D2 test goes red without it. * *

    THE MEASURED DEFECT. Both height-resolved lookups used to answer a height they had no binding * for by returning the HEAD registry - the key set in force right now. Below the arming height * that is correct and costs nothing: no certificate is being judged there. At and above it, it is * D2/T2 verbatim: "an armed node verifies a year-old header against the keys it holds today", so * one rotation makes every block between the arming height and the rotation unverifiable, and the * node reports success while doing it. Refusing is the only answer that does not assert a check * that was not performed. * *

    WHAT A REFUSAL COSTS AND WHY IT IS THE CHEAPER FAILURE. A null key makes every seal fail, so * {@code PqAnchorSealsRule} refuses the header and the node stops at the first block at or above * the arming height. It stops; it does not fork. A node that cannot say which keys were in force * cannot forge a competing history either, and the repair is to configure {@code * config.pqRegistryHash} plus {@code aere.falcon.registry.history} and restart. Compare the * silence this replaces, where the same node imports the whole chain and calls it verified. diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducer.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducer.java index 591ede15ed..8fd27c7a77 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducer.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducer.java @@ -26,115 +26,218 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Produces the non-Falcon scheme seals a hybrid commit carries, gated on height. */ public final class HybridSealProducer { private static final Logger LOG = LoggerFactory.getLogger(HybridSealProducer.class); /** The disarmed attachment height: no block ever reaches it, so nothing is ever emitted. */ public static final long NEVER = Long.MAX_VALUE; private final long attachFromBlock; private final PqSchemeSchedule schedule; private final int validatorIndex; private final Map localKeys; + // AERE AIP-22 stage 3b-ii: this node's NEXT keys per scheme (rotations), and its local index by height (from H_k the index of + // its Falcon seal at that height, since an extra seal must sit at the index of its Falcon seal) + private final Map> nextKeys; + private final java.util.function.LongToIntFunction localIndexAt; + private final Map> v3KeyByEpoch = new java.util.concurrent.ConcurrentHashMap<>(); + // the origin of every anchor grid of the chain (AIP-22 stage 2: per-scheme intervals count from it) + private final long anchorBlock; // one shout per missing scheme, not one per block: a permanent alarm is learned and ignored private final Set alreadyShouted = new java.util.HashSet<>(); /** + * A producer for schedules WITHOUT per-scheme intervals (every named scheme at every anchor), + * which is every schedule written before AIP-22 stage 2; the grid origin is irrelevant then. + * * @param attachFromBlock first height at which extras may be emitted; {@link #NEVER} to disarm * @param schedule which schemes are required at which height; null disarms as well * @param validatorIndex this node's index, written into every seal it produces * @param localKeys the private handles this node holds, per scheme id */ public HybridSealProducer( final long attachFromBlock, final PqSchemeSchedule schedule, final int validatorIndex, final Map localKeys) { + this(attachFromBlock, schedule, validatorIndex, localKeys, 0L); + } + + /** + * @param attachFromBlock first height at which extras may be emitted; {@link #NEVER} to disarm + * @param schedule which schemes are required at which height; null disarms as well + * @param validatorIndex this node's index, written into every seal it produces + * @param localKeys the private handles this node holds, per scheme id + * @param anchorBlock the chain's anchor activation height, origin of the per-scheme grids + */ + public HybridSealProducer( + final long attachFromBlock, + final PqSchemeSchedule schedule, + final int validatorIndex, + final Map localKeys, + final long anchorBlock) { + this.attachFromBlock = schedule == null ? NEVER : attachFromBlock; + this.schedule = schedule; + this.validatorIndex = validatorIndex; + this.localKeys = localKeys == null ? Map.of() : Map.copyOf(localKeys); + this.anchorBlock = anchorBlock; + this.nextKeys = Map.of(); + this.localIndexAt = h -> FalconSealSupport.instance().localIndexAt(h); + } + + /** + * AERE AIP-22 stage 3b-ii: a producer that also holds NEXT keys and resolves its index by height. + * + * @param attachFromBlock first height at which extras may be emitted; {@link #NEVER} to disarm + * @param schedule which schemes are required at which height; null disarms as well + * @param validatorIndex this node's index below H_k + * @param localKeys the private handles this node holds, per scheme id + * @param anchorBlock the chain's anchor activation height, origin of the per-scheme grids + * @param nextKeys the next private handles per scheme id, for v3 epochs that rotate this node's keys + * @param localIndexAt this node's index at a height (from H_k: the index of its Falcon seal there) + */ + public HybridSealProducer( + final long attachFromBlock, + final PqSchemeSchedule schedule, + final int validatorIndex, + final Map localKeys, + final long anchorBlock, + final Map> nextKeys, + final java.util.function.LongToIntFunction localIndexAt) { this.attachFromBlock = schedule == null ? NEVER : attachFromBlock; this.schedule = schedule; this.validatorIndex = validatorIndex; this.localKeys = localKeys == null ? Map.of() : Map.copyOf(localKeys); + this.anchorBlock = anchorBlock; + this.nextKeys = nextKeys == null ? Map.of() : Map.copyOf(nextKeys); + this.localIndexAt = localIndexAt; + } + + /** + * AERE AIP-22 stage 3b-ii: the key of a scheme this node signs with at a height from H_k, and the index it signs as: the local + * handle (base or next) whose probe signature verifies under the key of this node's row in the v3 epoch in force. + * + * @param scheme the scheme + * @param height the height + * @return the handle, or empty when this node holds no key of its row there (or has no row) + */ + public Optional v3KeyAt(final SealScheme scheme, final long height) { + final int index = localIndexAt.applyAsInt(height); + if (index < 0) { + return Optional.empty(); + } + final long bind = AerePqIdentity.epochAt(height).map(PqRegistryV3.Registry::bindHeight).orElse(-1L); + return v3KeyByEpoch.computeIfAbsent( + bind + ":" + index + ":" + scheme.id(), + k -> { + final Optional rowKey = AerePqIdentity.v3KeyAt(height, index, scheme.id()); + if (rowKey.isEmpty()) { + return Optional.empty(); + } + final List mine = new ArrayList<>(); + if (localKeys.get(scheme.id()) != null) { + mine.add(localKeys.get(scheme.id())); + } + mine.addAll(nextKeys.getOrDefault(scheme.id(), List.of())); + final byte[] probe = ("AERE-HYBRID-KEY-PROBE-V3:" + bind + ":" + index).getBytes(java.nio.charset.StandardCharsets.UTF_8); + for (final SealScheme.PrivateHandle h : mine) { + final Optional sig = scheme.sign(h, probe); + if (sig.isPresent() && scheme.verifyRaw(rowKey.get(), probe, sig.get())) { + return Optional.of(h); + } + } + return Optional.empty(); + }); } /** A producer that never emits anything: the configuration of every node today. */ public static HybridSealProducer disarmed() { return new HybridSealProducer(NEVER, null, -1, Map.of()); } /** * Whether extras may be emitted at this height at all. * * @param blockNumber the height * @return true when the attachment gate is open */ public boolean attachmentArmedAt(final long blockNumber) { return schedule != null && blockNumber >= attachFromBlock; } /** * The extra scheme seals for this block, or an empty list. * *

    Never throws: a producer fault must never take down the ECDSA commit path. Every refusal * is a logged reason plus an empty list, exactly the stance of the Falcon signer. * + *

    The block being committed is the PARENT of the anchor the seals serve (the caller only asks + * at anchor parents, D-329), so the schemes are those the anchor at {@code blockNumber + 1} + * requires: with per-scheme intervals (AIP-22 stage 2) a scheme on a coarser grid is not + * signed at the parent of an anchor that does not demand it. + * * @param blockNumber the height of the block being committed * @param message the very bytes the Falcon seal of this commit signs * @return the seals, or empty when the gate is shut, a key is missing, or signing failed */ public List sealsFor(final long blockNumber, final Bytes message) { if (!attachmentArmedAt(blockNumber) || message == null) { return List.of(); } try { - final Set required = schedule.schemesAt(blockNumber); + final Set required = schedule.schemesRequiredForAnchor(blockNumber + 1L, anchorBlock); final List produced = new ArrayList<>(); for (final String schemeId : required) { if (SealSchemes.FALCON_512.id().equals(schemeId)) { continue; // Falcon has its own slot and its own signer; never duplicated here } final Optional scheme = SealSchemes.byId(schemeId); if (scheme.isEmpty()) { shoutOnce(schemeId, "the schedule names scheme '" + schemeId + "' which this binary does not implement"); return List.of(); } - final SealScheme.PrivateHandle key = localKeys.get(schemeId); + // AERE AIP-22 stage 3b-ii: from H_k the key of this node's row in the v3 epoch in force, at its Falcon index there + final boolean v3 = AerePqIdentity.keysFromV3(blockNumber); + final SealScheme.PrivateHandle key = + v3 ? v3KeyAt(scheme.get(), blockNumber).orElse(null) : localKeys.get(schemeId); + final int index = v3 ? localIndexAt.applyAsInt(blockNumber) : validatorIndex; if (key == null) { shoutOnce(schemeId, "this node holds NO " + schemeId + " signing key, so it cannot produce the certificate the schedule requires from" + " height " + blockNumber + " onwards"); return List.of(); } final Optional signature = scheme.get().sign(key, message.toArray()); if (signature.isEmpty()) { shoutOnce(schemeId, "signing with the local " + schemeId + " key FAILED"); return List.of(); } produced.add( - new SchemeSeal(scheme.get().wireId(), validatorIndex, Bytes.wrap(signature.get()))); + new SchemeSeal(scheme.get().wireId(), index, Bytes.wrap(signature.get()))); } // Canonical order, so two honest nodes signing the same block emit identical bytes and the // certificate cannot become a source of gratuitous divergence. produced.sort(PqAnchorV2.CANONICAL); return List.copyOf(produced); } catch (final RuntimeException e) { LOG.warn("AERE HIBRID: producer fault at block {}, emitting nothing: {}", blockNumber, e.getMessage()); return List.of(); } } private void shoutOnce(final String schemeId, final String what) { if (alreadyShouted.add(schemeId)) { LOG.error("AERE HIBRID: {} - NO hybrid certificate will be emitted by this node." + " Fix this BEFORE the enforcement height, not on the day.", what); } } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealSupport.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealSupport.java index 34f362d8d6..2bb8679682 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealSupport.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/HybridSealSupport.java @@ -147,40 +147,60 @@ public final class HybridSealSupport { "AERE-PQC-COMMIT-CONF-03: " + PROPERTY_SCHEDULE + " and " + PROPERTY_REGISTRY + " are a PAIR; configure both or neither. Half a hybrid configuration must" + " refuse at startup, never run half-armed in silence."); } if (rawSchedule == null) { if (rawAttach != null) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-04: " + PROPERTY_ATTACH_BLOCK + " is set but the schedule and" + " registry are not: this node would EMIT seals nobody can verify."); } return new HybridSealSupport(null, null, HybridSealProducer.disarmed()); } final PqSchemeSchedule schedule; try { schedule = PqSchemeSchedule.parse(rawSchedule); } catch (final RuntimeException e) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-04: unparseable " + PROPERTY_SCHEDULE + ": " + e.getMessage()); } + // AIP-22 stage 2: a per-scheme interval must be a multiple of the anchor interval this node + // runs, otherwise the scheme would be demanded at heights that are not anchors at all, and + // the two grids are only known together here. Refuse at startup, never run half-armed. + final PqAnchorConfig anchorConfig = + org.hyperledger.besu.consensus.common.bft.blockcreation.PqAnchorProducer.config(); + if (schedule.hasIntervals() && anchorConfig.anchorInterval().isPresent()) { + final int base = anchorConfig.anchorInterval().getAsInt(); + for (final PqSchemeSchedule.Step step : schedule.steps()) { + for (final Map.Entry e : step.intervals().entrySet()) { + if (e.getValue() % base != 0) { + throw new IllegalStateException( + "AERE-PQC-HYBRID-CONF-06: " + PROPERTY_SCHEDULE + " gives scheme '" + e.getKey() + + "' the interval " + e.getValue() + " at " + step.fromBlock() + + ", which is not a multiple of this node's anchor interval " + base + + " (" + PqAnchorConfig.PROPERTY_ANCHOR_INTERVAL + "); the scheme would be" + + " demanded at heights that carry no certificate. Refusing to start."); + } + } + } + } final HybridSignerRegistry registry; try { final Properties p = new Properties(); p.load( new java.io.StringReader( new String(reader.file(rawRegistry), StandardCharsets.UTF_8))); registry = HybridSignerRegistry.fromProperties(p, rawRegistry); } catch (final IOException e) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-04: cannot read " + PROPERTY_REGISTRY + " '" + rawRegistry + "': " + e.getMessage()); } long attachFrom = HybridSealProducer.NEVER; if (rawAttach != null) { try { attachFrom = Long.parseLong(rawAttach.trim()); if (attachFrom < 0) { throw new NumberFormatException("negative"); } @@ -236,60 +256,128 @@ public final class HybridSealSupport { if (sig.isEmpty() || !scheme.verifyRaw(pub.get(), probe, sig.get())) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-05: the local " + scheme.id() + " key at index " + index + " does NOT verify against the registry's public key. Wrong key, wrong index," + " or wrong registry; refusing to start half-armed."); } if (boundIndex != null && boundIndex != index) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-05: local hybrid keys disagree on the validator index (" + boundIndex + " vs " + index + "). One node, one identity."); } boundIndex = index; keys.put(scheme.id(), handle); LOG.info( "AERE HIBRID: loaded local {} signing key for validator index {} (probe verified" + " against the registry)", scheme.id(), index); } + // AERE AIP-22 stage 3b-ii: this node's NEXT keys per scheme (aere.pq.hybrid.key..next, comma list, same file + // format). They belong to later v3 epochs, so no registry probe here: the startup guard checks each against its v3 row. + final Map> next = new HashMap<>(); + for (final SealScheme scheme : SealSchemes.all()) { + if (scheme.id().equals(SealSchemes.FALCON_512.id())) { + continue; + } + final String paths = reader.property(PROPERTY_KEY_PREFIX + scheme.id() + ".next"); + if (paths == null) { + continue; + } + for (final String np : com.google.common.base.Splitter.on(',').omitEmptyStrings().trimResults().split(paths)) { + if (np.isBlank()) { + continue; + } + try { + final Properties kp = new Properties(); + kp.load(new java.io.StringReader(new String(reader.file(np.trim()), StandardCharsets.UTF_8))); + final byte[] sk = Bytes.fromHexStringLenient(kp.getProperty("sk", "").trim()).toArray(); + next.computeIfAbsent(scheme.id(), x -> new java.util.ArrayList<>()) + .add(scheme.parsePrivateKey(sk).orElseThrow(() -> new IllegalStateException("bytes do not parse as a private key"))); + LOG.info("AERE HIBRID: loaded a NEXT {} signing key from {} (AIP-22 stage 3b-ii)", scheme.id(), np.trim()); + } catch (final IOException | RuntimeException e) { + throw new IllegalStateException( + "AERE-PQC-KEY-NEXT-01: cannot load the next " + scheme.id() + " key from '" + np.trim() + "': " + e.getMessage()); + } + } + } + if (attachFrom != HybridSealProducer.NEVER && keys.isEmpty()) { throw new IllegalStateException( "AERE-PQC-HYBRID-CONF-04: emission is armed from " + attachFrom + " but this node" + " holds no local hybrid key (" + PROPERTY_KEY_PREFIX + " unset)." + " It would promise a certificate it cannot produce."); } final HybridSealProducer producer = keys.isEmpty() ? HybridSealProducer.disarmed() - : new HybridSealProducer(attachFrom, schedule, boundIndex, keys); + : new HybridSealProducer( + attachFrom, + schedule, + boundIndex, + keys, + anchorConfig.anchorBlock(), + next, + h -> FalconSealSupport.instance().localIndexAt(h)); return new HybridSealSupport(schedule, registry, producer); } private static String firstOf(final ConfigReader r, final String prop, final String env) { final String p = r.property(prop); return p != null ? p : r.environment(env); } /** Returns the schedule, when the hybrid pair is configured. * * @return the schedule, or empty */ public Optional schedule() { return Optional.ofNullable(schedule); } /** Returns the registry, when the hybrid pair is configured. * * @return the registry, or empty */ public Optional registry() { return Optional.ofNullable(registry); } /** Returns the producer; disarmed (never emits) when nothing is configured. * * @return the producer */ public HybridSealProducer producer() { return producer; } + + /** + * AERE AIP-22 stage 3b-ii, THE STARTUP GUARD of the local hybrid keys: in every v3 epoch from H_k where this node's identity is + * registered, it must hold, for every non-Falcon scheme the schedule names at that epoch, the key of its row. Otherwise it would + * produce no hybrid seal from that epoch on while every configuration gate stays green; refuse to start instead. + * + * @param thisNodeAddress this node's own validator (ECDSA) address + */ + public void verifyLocalKeysCoverV3EpochsOrAbort(final org.hyperledger.besu.datatypes.Address thisNodeAddress) { + final Optional c = AerePqIdentity.installed(); + if (schedule == null || c.isEmpty() || c.get().keysFrom() == AerePqIdentity.NEVER || thisNodeAddress == null) { + return; + } + for (final PqRegistryV3.Registry e : c.get().epochs().tailMap(c.get().keysFrom(), true).values()) { + final org.hyperledger.besu.datatypes.Address me = AerePqIdentity.author(thisNodeAddress, e.bindHeight()); + if (e.rowOf(me.getBytes().toArrayUnsafe()).isEmpty()) { + continue; + } + for (final String schemeId : schedule.schemesAt(e.bindHeight())) { + if (schemeId.equals(SealSchemes.FALCON_512.id())) { + continue; + } + final Optional scheme = SealSchemes.byId(schemeId); + if (scheme.isEmpty() || producer.v3KeyAt(scheme.get(), e.bindHeight()).isEmpty()) { + throw new IllegalStateException( + "AERE-PQC-KEY-NEXT-02: REFUSING TO START. This node (id " + me + ") is registered in the v3 epoch bound at " + + e.bindHeight() + " but holds no " + schemeId + " key of its row there (aere.pq.hybrid.key." + schemeId + + "[.next]); from that height it would produce no hybrid seal."); + } + } + } + } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchor.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchor.java old mode 100755 new mode 100644 index 32c634ee85..5524158c03 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchor.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchor.java @@ -503,45 +503,46 @@ public final class PqAnchor { * commits in a {@code LinkedHashMap} keyed on author and {@code values()} yields arrival order, so * a producer that skips {@link #sortedByIndex(Collection)} writes an unsorted certificate and * nothing downstream notices: the anchor digest is taken IN THE ORDER GIVEN, so those headers' * digests are perfectly consistent with their own bytes. 36 such headers are canonical today. * *

    Nodes already holding the chain never revalidate them, which is exactly why this was invisible * for a day. A node syncing from genesis under the plain rule would reject header 13,267,824 and * stop there forever. That is the same shape as the base-fee floor episode: the history is what it * is, and the exception belongs at VALIDATION, where a header is accepted as written, not in any * recomputation. A patch anywhere else moves the error instead of ending it. * *

    What the exception does NOT relax. Inside the window the certificate must still have * non-negative and pairwise distinct indices. Measured 2026-08-11 on all 36 headers: distinct in * every one, no negative index in any. So this accepts exactly the history that exists and nothing * weaker, and the "repeat one seal to inflate k" attack stays unrepresentable at every height. * *

    Producers must not call this. {@code PqAnchorProducer} keeps requiring strict * sortedness, so no new unsorted header can ever be written, at any height. The window is closed by * construction: it is bounded above by a measured constant, so it cannot grow. * + * @param chainId the chain of the header; the unordered exception exists on chain 2800 only (D-428) * @param certificate the carried certificate * @param blockNumber the number of the header carrying it * @return true iff the ordering is acceptable for a header at that height */ public static boolean hasAcceptableIndices( - final Collection certificate, final long blockNumber) { + final long chainId, final Collection certificate, final long blockNumber) { if (hasStrictlyIncreasingIndices(certificate)) { return true; } - return PqAnchorLapse.isDisarmed(blockNumber) && hasDistinctNonNegativeIndices(certificate); + return PqAnchorLapse.isDisarmed(chainId, blockNumber) && hasDistinctNonNegativeIndices(certificate); } /** * Return the certificate sorted by validator index, ascending. Producers use this before writing * the certificate into extraData; validators never sort, they require sortedness. * * @param certificate the seals in any order * @return a new list sorted by validator index */ public static List sortedByIndex(final Collection certificate) { final List sorted = new ArrayList<>(certificate); sorted.sort(BY_INDEX); return sorted; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapse.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapse.java index a7c8c9bf1a..0c4cc207f8 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapse.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapse.java @@ -303,73 +303,89 @@ public final class PqAnchorLapse { private PqAnchorLapse() {} /** * Every named range, ordered by first height. * * @return an unmodifiable list, never null, possibly empty */ public static List windows() { return WINDOWS; } /** * Whether the anchor rules were out of force ENTIRELY at this height. * *

    This is the single question both anchor rules ask before they judge anything. They ask it * through this one method, for the same reason they share {@code anchorAppliesAt}: two rules that * each decided the boundary their own way could disagree about one header, and a header accepted * by one rule and refused by the other is a chain break. * *

    It is deliberately FALSE inside a threshold-only range. There the digest binding and the - * ordering did hold and are still required; only {@link #historicMinSeals(long)} moves. + * ordering did hold and are still required; only {@link #historicMinSeals(long, long)} moves. * + *

    AERE D-428 (2026-09-28): the ranges are facts about chain {@value #CHAIN_ID} and about no other chain. Before this the + * predicates did not take the chain, so a node of ANY other network built from this source turned the anchor rules off over + * the same heights, while the second client (PqAnchorLapse.cs) never did: two clients judging one header differently. + * + * @param chainId the chain of the header being judged * @param blockNumber the height being judged * @return true iff the height falls inside a range where nothing was in force */ - public static boolean isDisarmed(final long blockNumber) { + public static boolean isDisarmed(final long chainId, final long blockNumber) { + if (chainId != CHAIN_ID) { + return false; + } for (final Window window : WINDOWS) { if (window.covers(blockNumber) && window.relaxation() == Relaxation.EVERYTHING) { return true; } } return false; } /** * The seal threshold that was actually in force at this height, when it was lower than the * configured schedule. * *

    This is the same lever the fleet itself used at the time, the emergency ceiling, replayed * from a fixed range instead of from a runtime option. A node validating history therefore asks * the certificate for what was asked of the proposer that wrote it, and nothing more. * + * @param chainId the chain of the header being judged; any chain but {@value #CHAIN_ID} gets empty (D-428) * @param blockNumber the height being judged * @return the threshold in force, or empty when the configured schedule applies unchanged */ - public static OptionalInt historicMinSeals(final long blockNumber) { + public static OptionalInt historicMinSeals(final long chainId, final long blockNumber) { + if (chainId != CHAIN_ID) { + return OptionalInt.empty(); + } for (final Window window : WINDOWS) { if (window.covers(blockNumber) && window.relaxation() == Relaxation.THRESHOLD_ONLY) { return OptionalInt.of(window.effectiveMinSeals()); } } return OptionalInt.empty(); } /** * The range covering a height, when there is one. * + * @param chainId the chain of the header being judged; any chain but {@value #CHAIN_ID} gets empty (D-428) * @param blockNumber the height being judged * @return the range, or empty */ - public static Optional windowCovering(final long blockNumber) { + public static Optional windowCovering(final long chainId, final long blockNumber) { + if (chainId != CHAIN_ID) { + return Optional.empty(); + } return WINDOWS.stream().filter(w -> w.covers(blockNumber)).findFirst(); } /** * The first height of the earliest named range, or empty when there is none. * * @return the earliest affected height */ public static Optional earliestAffectedBlock() { return WINDOWS.stream().map(Window::firstBlock).min(Comparator.naturalOrder()); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryHashTool.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryHashTool.java index 14071c5e4e..3617715f3e 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryHashTool.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryHashTool.java @@ -151,95 +151,95 @@ public final class PqRegistryHashTool { return 2; } final long h = Long.parseLong(armingHeightRaw); final List files = files(o, "registry"); if (files.isEmpty()) { System.out.println("NOT MEASURED: --registry is missing"); return 2; } // The epochs: the first at the arming height, then one per --rotation =. final Map epoci = new LinkedHashMap<>(); epoci.put(h, files.get(0)); for (final String rot : list(o.get("rotation"))) { final int eq = rot.indexOf('='); if (eq < 0) { System.out.println("NOT MEASURED: --rotation is written =, got: " + rot); return 2; } epoci.put(Long.parseLong(rot.substring(0, eq).trim()), Path.of(rot.substring(eq + 1).trim())); } - final List inaltimi = new ArrayList<>(epoci.keySet()); - inaltimi.sort(Long::compare); - if (inaltimi.get(0) != h) { - System.out.println("RED: the first epoch is at " + inaltimi.get(0) + ", not at the arming height " + h); + final List epochHeights = new ArrayList<>(epoci.keySet()); + epochHeights.sort(Long::compare); + if (epochHeights.get(0) != h) { + System.out.println("RED: the first epoch is at " + epochHeights.get(0) + ", not at the arming height " + h); return 1; } // NO COMMENT INSIDE THE FRAGMENT, and that is a measurement, not a matter of taste. The first // form of this method put the file path after every entry, as `// /path/registry.json`. // The fragment LOOKS fine and CANNOT BE USED: genesis is read with Jackson without // ALLOW_COMMENTS, so a node handed one of those refuses to start with // [AERE-PQC-REG-LOAD-13] "Unexpected character ('/')", and it then refuses EVERY header from // the arming height upwards. Measured 2026-08-06, cases U3/U4 in d2-v2/dovezi/controale/. // Whatever is explanation is printed outside the JSON, on lines beginning with #. final StringBuilder json = new StringBuilder(); json.append(" \"pqRegistryHash\": [\n"); final StringBuilder history = new StringBuilder(); final StringBuilder harta = new StringBuilder(); - for (int i = 0; i < inaltimi.size(); i++) { - final long b = inaltimi.get(i); + for (int i = 0; i < epochHeights.size(); i++) { + final long b = epochHeights.get(i); final Path p = epoci.get(b); final PqRegistryHash.Registry reg = PqRegistryHash.loadAuto(p); // AERE D-C (2026-08-06). THIS IS THE DANGEROUS VERB: what is printed here gets pasted into // genesis, and genesis is the document all seven nodes hold identical. hashV1 next to a // registry that carries proofs writes into genesis a number the node's guard (hashFor) never // produces, so all seven nodes start, all report the registry loaded, and the chain stops at // H-1. A wrong indication is more dangerous than a missing one. final String hash = PqRegistryHash.hashFor(reg, chainId); // AERE D-B (2026-08-06). The recipe we print has to be the one the NEW code accepts. Since // 2026-08-06 the node refuses to start (AERE-PQC-REG-BIND-08) on a registry forced in at a // height its proofs did not sign. If the tool printed that recipe, it would manufacture // exactly the configuration the node rejects, and it would do so in a file that reaches all // seven at once. if (reg.proofBound() && reg.bindHeight() != b) { System.out.println( "RED: registry " + p + " has bind height " + reg.bindHeight() + ", while here it is scheduled from block " + b + ". The two numbers must be EQUAL: every row of the registry carries a proof of " + "possession and a claim, and both sign the bind height. A registry forced in from " + "another block is an activation day no validator signed, and the node refuses to " + "start on it with AERE-PQC-REG-BIND-08. It is not repaired by editing the " + "bindHeight field: the proofs are over it, and the file would no longer load at " + "all (AERE-PQC-REG-BIND-03). It is repaired either by scheduling the epoch at " + reg.bindHeight() + ", or by re-signing the registry for " + b + ", which means the key ceremony and a NEW hash."); return 1; } json.append(" {\"block\": ").append(b).append(", \"hash\": \"0x").append(hash).append("\"}"); - if (i < inaltimi.size() - 1) { + if (i < epochHeights.size() - 1) { json.append(','); } json.append('\n'); harta.append("# block ").append(b).append(" -> ").append(p.toAbsolutePath()).append('\n'); if (history.length() > 0) { history.append(','); } history.append(p.toAbsolutePath()); } json.append(" ]"); System.out.println("# 1. In the genesis of chain " + chainId + ", inside the \"config\" object:"); System.out.println("# The fragment below is VALID JSON and is pasted as it stands. No comments"); System.out.println("# are added to it: genesis is read WITHOUT ALLOW_COMMENTS and the node refuses to start."); System.out.println(json); System.out.println("# Which registry file stands behind each epoch:"); System.out.print(harta); System.out.println(); System.out.println("# 2. On EVERY node, the second client included, in BESU_OPTS:"); System.out.println("-Daere.falcon.registry.history=" + history); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3.java new file mode 100644 index 0000000000..3fb8a5c543 --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3.java @@ -0,0 +1,963 @@ +/* + * AERE AIP-22 stage 3: the validator registry v3 (post-quantum identity). + * + * WHAT IT IS. One registry epoch as a JSON manifest: rows keyed "0".."count-1" plus formatVersion 3, + * chainId, bindHeight and count at top level. Each row names a validator by a 20-byte `id` derived + * from its Falcon-512 key (AIP-22 section 3.1, the AIP-20 sender derivation with alg id 0x20): + * + * id = keccak256(0x50 || 0x20 || falcon512_public_key_897)[12:32] + * + * and binds to it one public key per seal scheme, each with a proof of possession. The id is taken + * at the first registration and KEPT through key rotations: a later epoch may change a row's keys + * only with a rotation signature by EVERY key the previous epoch bound to that id. The `ecdsa` + * field ({addr, claim}) is the verifiable link from a classical validator address to the id, and is + * required in the epoch that performs the switch at H_id. `transport` names the RLPx node key a + * validator's client uses, for the peer-forwarding predicate (AIP-22 section 3.4). + * + * THE CANONICAL FORMS, pinned here and in the conformance vectors (AIP-22 section 3.2 names them; + * this class is where their bytes are fixed, and the Nethermind port and the public JS tool must + * produce the same bytes from the same vector files): + * + * row pre-image = "AERE-PQ-ROW-3" || u8(3) || u64be(chainId) || u64be(bindHeight) || u32be(count) + * || u32be(index) || id(20) || u8(nKeys) + * || for each key in ascending wire id: u8(wireId) || u32be(len pk) || pk + * || u32be(len pop) || pop + * || u8(hasTransport) [|| transport(20)] + * row hash = keccak256(row pre-image) (rotation and ecdsa are NOT in it) + * Merkle root = binary tree over the row hashes in index order; node = keccak256(0x01 || l || r); + * an odd node at any level is paired with itself; one row: the root is its hash + * registry hash = keccak256("AERE-PQ-REGISTRY-3" || u64be(chainId) || u64be(bindHeight) + * || u32be(count) || root) + * pop message = keccak256("AERE-PQ-POP-3" || u8(3) || u64be(chainId) || u64be(bindHeight) + * || u32be(count) || u32be(index) || id(20) || u8(wireId) || u32be(len pk) || pk) + * rotate message = keccak256("AERE-PQ-ROTATE-3" || new row hash) + * claim digest = keccak256(0x19 || 0x00 || context || new row hash), context = the low 20 bytes + * of keccak256("AERE-PQ-CLAIM-1" || u64be(chainId)), the EIP-191 envelope of v2 + * + * The public key in the pre-images is the STANDARD encoding: Falcon-512 897 bytes (header 0x09 + + * the 896-byte h polynomial the v1/v2 registries store), SLH-DSA-SHA2-128s 32 bytes. Every field + * of the pop message earns its place for the reason given in PqRegistryBinding (domain tag against + * seal replay, key in the message against Falcon's missing exclusive ownership, chainId, index, + * bindHeight and count against lifting and truncation); the wire id keeps one scheme's proof from + * standing in for another's. The Merkle node tag 0x01 keeps an inner node from ever being read as + * a row hash, and the count bound into the registry hash keeps a duplicated odd leaf from making a + * tree of count+1 rows hash like one of count. + * + * LOADING IS STRICT, as for v1/v2: an ambiguous or incomplete file REFUSES with a named code, never + * hashes. Refusal codes AERE-PQC-REG3-01..16, listed at {@link Code}. + * + * WHAT IT DOES NOT DO (stated): it does not wire the ids into QBFT (the set translation at H_id, + * message authorship, coinbase, votes and peer forwarding are later parts of stage 3), and it + * does not pin the registry hash on chain (the anchor-contract slot or genesis pqRegistryHash + * does that, as today; {@link #loadPinned} compares against the pinned value). + */ +package org.hyperledger.besu.consensus.common.bft; + +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Util; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.bouncycastle.crypto.digests.KeccakDigest; + +/** The v3 validator registry: canonical forms, strict loader, Merkle membership. */ +public final class PqRegistryV3 { + + /** Format version of this registry. */ + public static final int FORMAT_VERSION = 3; + + /** Domain tag of the row pre-image. */ + public static final String DOMAIN_ROW = "AERE-PQ-ROW-3"; + + /** Domain tag of the registry hash. */ + public static final String DOMAIN_REGISTRY = "AERE-PQ-REGISTRY-3"; + + /** Domain tag of the proof-of-possession message. */ + public static final String DOMAIN_POP = "AERE-PQ-POP-3"; + + /** Domain tag of the rotation message. */ + public static final String DOMAIN_ROTATE = "AERE-PQ-ROTATE-3"; + + /** Tag byte of an inner Merkle node. */ + public static final byte MERKLE_NODE_TAG = 0x01; + + /** First byte of the id pre-image: the AIP-20 transaction type. */ + public static final byte ID_TX_TYPE = 0x50; + + /** Second byte of the id pre-image: the algorithm id AIP-20 reserves for Falcon-512. */ + public static final byte ID_ALG_FALCON_512 = 0x20; + + /** Header byte of the standard Falcon-512 public key encoding. */ + public static final byte FALCON_512_PK_HEADER = 0x09; + + /** Standard Falcon-512 public key length (header + 896-byte h). */ + public static final int FALCON_512_PK_LENGTH = 897; + + private static final int ECDSA_SIGNATURE_LENGTH = 65; + + /** The named refusals of the strict loader. */ + public enum Code { + /** formatVersion missing or not 3. */ + REG3_01_FORMAT("AERE-PQC-REG3-01"), + /** chainId missing or not the node's chain. */ + REG3_02_CHAIN("AERE-PQC-REG3-02"), + /** count missing, rows not exactly "0".."count-1", or an unknown top-level or row field. */ + REG3_03_SHAPE("AERE-PQC-REG3-03"), + /** id missing, not hex, or not 20 bytes. */ + REG3_04_ID("AERE-PQC-REG3-04"), + /** a key names a scheme this build does not know. */ + REG3_05_UNKNOWN_SCHEME("AERE-PQC-REG3-05"), + /** a scheme the schedule requires has no key on a row. */ + REG3_06_REQUIRED_SCHEME("AERE-PQC-REG3-06"), + /** a public key is not in its scheme's standard encoding. */ + REG3_07_KEY_ENCODING("AERE-PQC-REG3-07"), + /** a proof of possession is missing or does not verify. */ + REG3_08_POP("AERE-PQC-REG3-08"), + /** a NEW id is not the derivation of the row's Falcon-512 key. */ + REG3_09_ID_DERIVATION("AERE-PQC-REG3-09"), + /** two rows carry the same id. */ + REG3_10_DUPLICATE_ID("AERE-PQC-REG3-10"), + /** two rows carry the same public key. */ + REG3_11_DUPLICATE_KEY("AERE-PQC-REG3-11"), + /** a changed row lacks a verifying rotation signature by every previous key of its id. */ + REG3_12_ROTATION_MISSING("AERE-PQC-REG3-12"), + /** a rotation is present where none belongs (unchanged row, or a new id). */ + REG3_13_ROTATION_UNEXPECTED("AERE-PQC-REG3-13"), + /** the ecdsa link is required and missing, does not recover to its address, or repeats one. */ + REG3_14_ECDSA("AERE-PQC-REG3-14"), + /** transport is malformed. */ + REG3_15_TRANSPORT("AERE-PQC-REG3-15"), + /** the computed registry hash is not the pinned (or the file's own declared) hash. */ + REG3_16_HASH("AERE-PQC-REG3-16"); + + private final String text; + + Code(final String text) { + this.text = text; + } + + /** + * The refusal code as printed. + * + * @return e.g. AERE-PQC-REG3-08 + */ + public String text() { + return text; + } + } + + /** A refusal of the strict loader, carrying its named code. */ + public static final class RegistryV3Exception extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** The named refusal. */ + private final transient Code code; + + /** + * A refusal. + * + * @param code the named code + * @param message what was wrong, in words + */ + public RegistryV3Exception(final Code code, final String message) { + super(code.text() + ": " + message); + this.code = code; + } + + /** + * The named code. + * + * @return the code + */ + public Code code() { + return code; + } + } + + /** + * One key of a row. + * + * @param scheme the seal scheme + * @param publicKey the standard encoding (Falcon-512 897 bytes, SLH-DSA-SHA2-128s 32 bytes) + * @param pop the proof of possession by that key + */ + public record Key(SealScheme scheme, byte[] publicKey, byte[] pop) {} + + /** + * One row. + * + * @param index the row index + * @param id the 20-byte validator id + * @param keys the keys by wire id, ascending + * @param transport the RLPx node-key address, or null + * @param rotation scheme id to rotation signature (by the previous epoch's key of that scheme) + * @param ecdsaAddress the classical validator address, or null + * @param ecdsaClaim the EIP-191 claim by that address over the row hash, or null + */ + public record Row( + int index, + byte[] id, + TreeMap keys, + byte[] transport, + Map rotation, + byte[] ecdsaAddress, + byte[] ecdsaClaim) { + + /** + * The key of a scheme on this row. + * + * @param schemeId e.g. "falcon-512" + * @return the key, if the row carries one + */ + public Optional key(final String schemeId) { + for (final Key k : keys.values()) { + if (k.scheme().id().equals(schemeId)) { + return Optional.of(k); + } + } + return Optional.empty(); + } + } + + /** + * A loaded, verified epoch. + * + * @param chainId the chain + * @param bindHeight the activation height of this epoch + * @param count the number of rows + * @param rows the rows in index order + * @param rowHashes the row hashes in index order + * @param root the Merkle root over the row hashes + * @param hash the registry hash (what the chain pins) + */ + public record Registry( + long chainId, + long bindHeight, + int count, + List rows, + List rowHashes, + Bytes32 root, + Bytes32 hash) { + + /** + * The row of an id. + * + * @param id the 20-byte id + * @return the row, if the id is registered in this epoch + */ + public Optional rowOf(final byte[] id) { + for (final Row r : rows) { + if (Arrays.equals(r.id(), id)) { + return Optional.of(r); + } + } + return Optional.empty(); + } + + /** + * The row whose ecdsa link names this classical address. + * + * @param address the 20-byte address + * @return the row, if one links it + */ + public Optional rowOfEcdsa(final byte[] address) { + for (final Row r : rows) { + if (r.ecdsaAddress() != null && Arrays.equals(r.ecdsaAddress(), address)) { + return Optional.of(r); + } + } + return Optional.empty(); + } + } + + private PqRegistryV3() {} + + // ============================================================================================= + // Canonical forms + // ============================================================================================= + + /** + * The validator id of a Falcon-512 key (AIP-22 section 3.1). + * + * @param falconStandard897 the 897-byte standard encoding + * @return the 20-byte id + */ + public static byte[] idOf(final byte[] falconStandard897) { + final byte[] in = new byte[2 + falconStandard897.length]; + in[0] = ID_TX_TYPE; + in[1] = ID_ALG_FALCON_512; + System.arraycopy(falconStandard897, 0, in, 2, falconStandard897.length); + return Arrays.copyOfRange(keccak(in), 12, 32); + } + + /** + * The message a row key signs to prove possession. + * + * @param chainId the chain + * @param bindHeight the epoch's activation height + * @param count the number of rows + * @param index the row index + * @param id the row id + * @param wireId the scheme wire id + * @param publicKey the standard encoding of the key + * @return the 32-byte message + */ + public static Bytes32 popMessage( + final long chainId, + final long bindHeight, + final int count, + final int index, + final byte[] id, + final byte wireId, + final byte[] publicKey) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeAll(out, DOMAIN_POP.getBytes(StandardCharsets.US_ASCII)); + out.write(FORMAT_VERSION); + writeAll(out, uint64be(chainId)); + writeAll(out, uint64be(bindHeight)); + writeAll(out, uint32be(count)); + writeAll(out, uint32be(index)); + writeAll(out, id); + out.write(wireId); + writeAll(out, uint32be(publicKey.length)); + writeAll(out, publicKey); + return Bytes32.wrap(keccak(out.toByteArray())); + } + + /** + * The canonical pre-image of a row (without rotation and ecdsa). + * + * @param chainId the chain + * @param bindHeight the epoch's activation height + * @param count the number of rows + * @param row the row + * @return the pre-image + */ + public static byte[] rowPreimage( + final long chainId, final long bindHeight, final int count, final Row row) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeAll(out, DOMAIN_ROW.getBytes(StandardCharsets.US_ASCII)); + out.write(FORMAT_VERSION); + writeAll(out, uint64be(chainId)); + writeAll(out, uint64be(bindHeight)); + writeAll(out, uint32be(count)); + writeAll(out, uint32be(row.index())); + writeAll(out, row.id()); + out.write(row.keys().size()); + for (final Map.Entry e : row.keys().entrySet()) { + out.write(e.getKey()); + writeAll(out, uint32be(e.getValue().publicKey().length)); + writeAll(out, e.getValue().publicKey()); + writeAll(out, uint32be(e.getValue().pop().length)); + writeAll(out, e.getValue().pop()); + } + if (row.transport() == null) { + out.write(0); + } else { + out.write(1); + writeAll(out, row.transport()); + } + return out.toByteArray(); + } + + /** + * The row hash. + * + * @param chainId the chain + * @param bindHeight the epoch's activation height + * @param count the number of rows + * @param row the row + * @return keccak256 of the row pre-image + */ + public static Bytes32 rowHash( + final long chainId, final long bindHeight, final int count, final Row row) { + return Bytes32.wrap(keccak(rowPreimage(chainId, bindHeight, count, row))); + } + + /** + * The Merkle root over row hashes in index order. + * + * @param leaves the row hashes + * @return the root + */ + public static Bytes32 merkleRoot(final List leaves) { + if (leaves.isEmpty()) { + throw new IllegalArgumentException("a registry has at least one row"); + } + List level = leaves; + while (level.size() > 1) { + final List next = new ArrayList<>(); + for (int i = 0; i < level.size(); i += 2) { + final Bytes32 l = level.get(i); + final Bytes32 r = i + 1 < level.size() ? level.get(i + 1) : l; + next.add(node(l, r)); + } + level = next; + } + return level.get(0); + } + + /** + * The sibling path of one leaf, bottom-up. + * + * @param leaves the row hashes + * @param index the leaf index + * @return the siblings from the leaf level up + */ + public static List merkleProof(final List leaves, final int index) { + if (index < 0 || index >= leaves.size()) { + throw new IllegalArgumentException("index " + index + " is not a row of " + leaves.size()); + } + final List proof = new ArrayList<>(); + List level = leaves; + int i = index; + while (level.size() > 1) { + final int sib = (i % 2 == 0) ? i + 1 : i - 1; + proof.add(sib < level.size() ? level.get(sib) : level.get(i)); + final List next = new ArrayList<>(); + for (int j = 0; j < level.size(); j += 2) { + final Bytes32 l = level.get(j); + final Bytes32 r = j + 1 < level.size() ? level.get(j + 1) : l; + next.add(node(l, r)); + } + level = next; + i /= 2; + } + return proof; + } + + /** + * Checks a membership proof. The index MUST be below count: with the odd leaf paired with + * itself, a path for index == count would otherwise verify against a count-row tree. + * + * @param leaf the row hash + * @param index the claimed index + * @param count the number of rows + * @param proof the siblings, bottom-up + * @param root the root + * @return whether the leaf is row {@code index} of the tree with that root + */ + public static boolean verifyMerkleProof( + final Bytes32 leaf, + final int index, + final int count, + final List proof, + final Bytes32 root) { + if (index < 0 || index >= count || count < 1) { + return false; + } + int depth = 0; + for (int n = count; n > 1; n = (n + 1) / 2) { + depth++; + } + if (proof.size() != depth) { + return false; + } + Bytes32 acc = leaf; + int i = index; + for (final Bytes32 sib : proof) { + acc = (i % 2 == 0) ? node(acc, sib) : node(sib, acc); + i /= 2; + } + return acc.equals(root); + } + + /** + * The registry hash the chain pins. + * + * @param chainId the chain + * @param bindHeight the epoch's activation height + * @param count the number of rows + * @param root the Merkle root + * @return the hash + */ + public static Bytes32 registryHash( + final long chainId, final long bindHeight, final int count, final Bytes32 root) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeAll(out, DOMAIN_REGISTRY.getBytes(StandardCharsets.US_ASCII)); + writeAll(out, uint64be(chainId)); + writeAll(out, uint64be(bindHeight)); + writeAll(out, uint32be(count)); + writeAll(out, root.toArrayUnsafe()); + return Bytes32.wrap(keccak(out.toByteArray())); + } + + /** + * The message every previous key of an id signs to authorise a changed row. + * + * @param newRowHash the hash of the new row + * @return the 32-byte message + */ + public static Bytes32 rotateMessage(final Bytes32 newRowHash) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeAll(out, DOMAIN_ROTATE.getBytes(StandardCharsets.US_ASCII)); + writeAll(out, newRowHash.toArrayUnsafe()); + return Bytes32.wrap(keccak(out.toByteArray())); + } + + /** + * The digest the classical validator key signs to link its address to the row. + * + * @param chainId the chain + * @param rowHash the row hash + * @return the EIP-191 version-0 digest + */ + public static Bytes32 claimDigest(final long chainId, final Bytes32 rowHash) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x19); + out.write(0x00); + writeAll(out, PqRegistryBinding.claimContext(chainId)); + writeAll(out, rowHash.toArrayUnsafe()); + return Bytes32.wrap(keccak(out.toByteArray())); + } + + // ============================================================================================= + // The strict loader + // ============================================================================================= + + /** + * Loads and verifies an epoch and compares its hash with the pinned one. + * + * @param json the manifest + * @param chainId the node's chain + * @param requiredSchemes the scheme ids every row must carry + * @param previous the previous v3 epoch (for rotations), or null for the first + * @param ecdsaRequired whether every row must carry the ecdsa link (the switch epoch) + * @param pinnedHash the hash the chain pins + * @return the verified epoch + */ + public static Registry loadPinned( + final JsonNode json, + final long chainId, + final List requiredSchemes, + final Registry previous, + final boolean ecdsaRequired, + final Bytes32 pinnedHash) { + final Registry r = load(json, chainId, requiredSchemes, previous, ecdsaRequired); + if (!r.hash().equals(pinnedHash)) { + throw new RegistryV3Exception( + Code.REG3_16_HASH, + "the registry hashes to " + r.hash() + ", the chain pins " + pinnedHash + "; refusing"); + } + return r; + } + + /** + * Loads and verifies an epoch (every proof, every uniqueness rule, every rotation). + * + * @param json the manifest + * @param chainId the node's chain + * @param requiredSchemes the scheme ids every row must carry + * @param previous the previous v3 epoch (for rotations), or null for the first + * @param ecdsaRequired whether every row must carry the ecdsa link + * @return the verified epoch + */ + public static Registry load( + final JsonNode json, + final long chainId, + final List requiredSchemes, + final Registry previous, + final boolean ecdsaRequired) { + if (json == null || !json.isObject()) { + throw new RegistryV3Exception(Code.REG3_03_SHAPE, "the manifest is not a JSON object"); + } + if (!json.has("formatVersion") || json.get("formatVersion").asInt(-1) != FORMAT_VERSION + || !json.get("formatVersion").isIntegralNumber()) { + throw new RegistryV3Exception(Code.REG3_01_FORMAT, "formatVersion must be the number 3"); + } + if (!json.has("chainId") || !json.get("chainId").isIntegralNumber() + || json.get("chainId").asLong() != chainId) { + throw new RegistryV3Exception( + Code.REG3_02_CHAIN, "chainId must be the number " + chainId + " (this node's chain)"); + } + if (!json.has("bindHeight") || !json.get("bindHeight").isIntegralNumber() + || json.get("bindHeight").asLong() < 0) { + throw new RegistryV3Exception(Code.REG3_03_SHAPE, "bindHeight must be a number >= 0"); + } + if (!json.has("count") || !json.get("count").isIntegralNumber() + || json.get("count").asInt() < 1) { + throw new RegistryV3Exception(Code.REG3_03_SHAPE, "count must be a number >= 1"); + } + final long bindHeight = json.get("bindHeight").asLong(); + final int count = json.get("count").asInt(); + final Set allowedTop = new HashSet<>(); + Collections.addAll(allowedTop, "formatVersion", "chainId", "bindHeight", "count", "registryHash"); + for (int i = 0; i < count; i++) { + allowedTop.add(Integer.toString(i)); + } + final Iterator names = json.fieldNames(); + while (names.hasNext()) { + final String n = names.next(); + if (!allowedTop.contains(n)) { + throw new RegistryV3Exception( + Code.REG3_03_SHAPE, + "unexpected top-level field '" + n + "' (rows are exactly \"0\"..\"" + (count - 1) + + "\")"); + } + } + final List rows = new ArrayList<>(); + for (int i = 0; i < count; i++) { + final JsonNode e = json.get(Integer.toString(i)); + if (e == null || !e.isObject()) { + throw new RegistryV3Exception(Code.REG3_03_SHAPE, "row " + i + " is missing"); + } + rows.add(parseRow(i, e)); + } + // required schemes, key encodings, possession proofs + for (final Row row : rows) { + for (final String s : requiredSchemes) { + if (row.key(s).isEmpty()) { + throw new RegistryV3Exception( + Code.REG3_06_REQUIRED_SCHEME, "row " + row.index() + " has no " + s + " key"); + } + } + if (row.key(SealSchemes.FALCON_512.id()).isEmpty()) { + throw new RegistryV3Exception( + Code.REG3_06_REQUIRED_SCHEME, + "row " + row.index() + " has no falcon-512 key; the id is derived from it"); + } + for (final Key k : row.keys().values()) { + final byte[] registryForm = registryForm(row.index(), k); + final Bytes32 msg = + popMessage( + chainId, bindHeight, count, row.index(), row.id(), k.scheme().wireId(), + k.publicKey()); + if (k.pop() == null || k.pop().length == 0 + || !k.scheme().verifyRaw(registryForm, msg.toArrayUnsafe(), k.pop())) { + throw new RegistryV3Exception( + Code.REG3_08_POP, + "row " + row.index() + " " + k.scheme().id() + + ": the proof of possession does not verify for chainId=" + chainId + + " bindHeight=" + bindHeight + " count=" + count + " index=" + row.index()); + } + } + } + // uniqueness of ids and of every public key + final Set ids = new HashSet<>(); + final Set keys = new HashSet<>(); + for (final Row row : rows) { + if (!ids.add(Bytes.wrap(row.id()))) { + throw new RegistryV3Exception( + Code.REG3_10_DUPLICATE_ID, "id 0x" + hex(row.id()) + " appears on two rows"); + } + for (final Key k : row.keys().values()) { + if (!keys.add(Bytes.wrap(k.publicKey()))) { + throw new RegistryV3Exception( + Code.REG3_11_DUPLICATE_KEY, + "row " + row.index() + " " + k.scheme().id() + ": the key appears on another row"); + } + } + } + // row hashes, then ids and rotations against the previous epoch + final List hashes = new ArrayList<>(); + for (final Row row : rows) { + hashes.add(rowHash(chainId, bindHeight, count, row)); + } + for (final Row row : rows) { + final Optional prev = previous == null ? Optional.empty() : previous.rowOf(row.id()); + if (prev.isEmpty()) { + final byte[] derived = idOf(row.key(SealSchemes.FALCON_512.id()).orElseThrow().publicKey()); + if (!Arrays.equals(derived, row.id())) { + throw new RegistryV3Exception( + Code.REG3_09_ID_DERIVATION, + "row " + row.index() + ": a new id must be keccak256(0x50||0x20||falcon)[12:32] = 0x" + + hex(derived) + ", the row says 0x" + hex(row.id())); + } + if (!row.rotation().isEmpty()) { + throw new RegistryV3Exception( + Code.REG3_13_ROTATION_UNEXPECTED, + "row " + row.index() + " is a new id and carries a rotation"); + } + continue; + } + final boolean changed = !sameKeys(prev.get(), row); + if (!changed) { + if (!row.rotation().isEmpty()) { + throw new RegistryV3Exception( + Code.REG3_13_ROTATION_UNEXPECTED, + "row " + row.index() + " did not change its keys and carries a rotation"); + } + continue; + } + final Bytes32 rot = rotateMessage(hashes.get(row.index())); + for (final Key old : prev.get().keys().values()) { + final byte[] sig = row.rotation().get(old.scheme().id()); + if (sig == null + || !old.scheme() + .verifyRaw(registryForm(prev.get().index(), old), rot.toArrayUnsafe(), sig)) { + throw new RegistryV3Exception( + Code.REG3_12_ROTATION_MISSING, + "row " + row.index() + " changed its keys; the previous " + old.scheme().id() + + " key of id 0x" + hex(row.id()) + " did not sign the new row (every previous" + + " key must)"); + } + } + if (row.rotation().size() != prev.get().keys().size()) { + throw new RegistryV3Exception( + Code.REG3_13_ROTATION_UNEXPECTED, + "row " + row.index() + " carries a rotation signature for a scheme the previous row" + + " did not bind"); + } + } + // the ecdsa link + final Set ecdsa = new HashSet<>(); + for (final Row row : rows) { + if (row.ecdsaAddress() == null) { + if (ecdsaRequired) { + throw new RegistryV3Exception( + Code.REG3_14_ECDSA, + "row " + row.index() + " has no ecdsa link; this epoch performs the switch and" + + " every row must carry one"); + } + continue; + } + final Address rec = recover(claimDigest(chainId, hashes.get(row.index())), row.ecdsaClaim()); + if (rec == null || !Arrays.equals(rec.getBytes().toArrayUnsafe(), row.ecdsaAddress())) { + throw new RegistryV3Exception( + Code.REG3_14_ECDSA, + "row " + row.index() + ": the ecdsa claim does not recover to 0x" + + hex(row.ecdsaAddress())); + } + if (!ecdsa.add(Bytes.wrap(row.ecdsaAddress()))) { + throw new RegistryV3Exception( + Code.REG3_14_ECDSA, "row " + row.index() + ": the ecdsa address appears on two rows"); + } + } + final Bytes32 root = merkleRoot(hashes); + final Bytes32 hash = registryHash(chainId, bindHeight, count, root); + if (json.has("registryHash")) { + final Bytes32 declared = parseBytes32(json.get("registryHash"), "registryHash"); + if (!declared.equals(hash)) { + throw new RegistryV3Exception( + Code.REG3_16_HASH, + "the file declares registryHash " + declared + " and hashes to " + hash); + } + } + return new Registry( + chainId, + bindHeight, + count, + Collections.unmodifiableList(rows), + Collections.unmodifiableList(hashes), + root, + hash); + } + + // ============================================================================================= + // Parsing + // ============================================================================================= + + private static Row parseRow(final int index, final JsonNode e) { + final Set allowed = Set.of("id", "keys", "pop", "rotation", "ecdsa", "transport"); + final Iterator fields = e.fieldNames(); + while (fields.hasNext()) { + final String f = fields.next(); + if (!allowed.contains(f)) { + throw new RegistryV3Exception( + Code.REG3_03_SHAPE, "row " + index + " has an unknown field '" + f + "'"); + } + } + final byte[] id = parseHex(e.get("id"), 20, Code.REG3_04_ID, "row " + index + " id"); + final JsonNode keysNode = e.get("keys"); + final JsonNode popNode = e.get("pop"); + if (keysNode == null || !keysNode.isObject() || keysNode.size() == 0) { + throw new RegistryV3Exception(Code.REG3_03_SHAPE, "row " + index + " has no keys"); + } + if (popNode == null || !popNode.isObject()) { + throw new RegistryV3Exception(Code.REG3_08_POP, "row " + index + " has no pop object"); + } + final TreeMap keys = new TreeMap<>(); + final Iterator> it = keysNode.fields(); + while (it.hasNext()) { + final Map.Entry k = it.next(); + final SealScheme scheme = + SealSchemes.byId(k.getKey()) + .orElseThrow( + () -> + new RegistryV3Exception( + Code.REG3_05_UNKNOWN_SCHEME, + "row " + index + " names the unknown scheme '" + k.getKey() + "'")); + final int len = + scheme == SealSchemes.FALCON_512 ? FALCON_512_PK_LENGTH : scheme.publicKeyLength(); + final byte[] pk = + parseHex(k.getValue(), len, Code.REG3_07_KEY_ENCODING, "row " + index + " " + k.getKey()); + if (scheme == SealSchemes.FALCON_512 && pk[0] != FALCON_512_PK_HEADER) { + throw new RegistryV3Exception( + Code.REG3_07_KEY_ENCODING, + "row " + index + " falcon-512: the standard encoding starts with 0x09"); + } + final JsonNode p = popNode.get(k.getKey()); + if (p == null) { + throw new RegistryV3Exception( + Code.REG3_08_POP, "row " + index + " " + k.getKey() + " has no proof of possession"); + } + final byte[] pop = parseHex(p, -1, Code.REG3_08_POP, "row " + index + " pop " + k.getKey()); + keys.put(scheme.wireId() & 0xff, new Key(scheme, pk, pop)); + } + if (popNode.size() != keysNode.size()) { + throw new RegistryV3Exception( + Code.REG3_08_POP, "row " + index + " carries a proof for a scheme it has no key for"); + } + byte[] transport = null; + final JsonNode t = e.get("transport"); + if (t != null) { + if (!t.isObject() || t.size() != 1 || !t.has("secp256k1Address")) { + throw new RegistryV3Exception( + Code.REG3_15_TRANSPORT, "row " + index + " transport must be {secp256k1Address}"); + } + transport = + parseHex(t.get("secp256k1Address"), 20, Code.REG3_15_TRANSPORT, "row " + index + " transport"); + } + final Map rotation = new LinkedHashMap<>(); + final JsonNode rot = e.get("rotation"); + if (rot != null) { + if (!rot.isObject() || rot.size() == 0) { + throw new RegistryV3Exception( + Code.REG3_13_ROTATION_UNEXPECTED, "row " + index + " rotation must be a non-empty map"); + } + final Iterator> ri = rot.fields(); + while (ri.hasNext()) { + final Map.Entry r = ri.next(); + if (SealSchemes.byId(r.getKey()).isEmpty()) { + throw new RegistryV3Exception( + Code.REG3_05_UNKNOWN_SCHEME, + "row " + index + " rotation names the unknown scheme '" + r.getKey() + "'"); + } + rotation.put( + r.getKey(), + parseHex(r.getValue(), -1, Code.REG3_12_ROTATION_MISSING, "row " + index + " rotation")); + } + } + byte[] ecdsaAddress = null; + byte[] ecdsaClaim = null; + final JsonNode ec = e.get("ecdsa"); + if (ec != null) { + if (!ec.isObject() || ec.size() != 2 || !ec.has("addr") || !ec.has("claim")) { + throw new RegistryV3Exception( + Code.REG3_14_ECDSA, "row " + index + " ecdsa must be {addr, claim}"); + } + ecdsaAddress = parseHex(ec.get("addr"), 20, Code.REG3_14_ECDSA, "row " + index + " ecdsa.addr"); + ecdsaClaim = + parseHex( + ec.get("claim"), ECDSA_SIGNATURE_LENGTH, Code.REG3_14_ECDSA, "row " + index + " claim"); + } + return new Row(index, id, keys, transport, rotation, ecdsaAddress, ecdsaClaim); + } + + /** The form a scheme's parser takes: Falcon without the header byte, others as stored. */ + private static byte[] registryForm(final int index, final Key k) { + if (k.scheme() == SealSchemes.FALCON_512) { + if (k.publicKey().length != FALCON_512_PK_LENGTH) { + throw new RegistryV3Exception( + Code.REG3_07_KEY_ENCODING, "row " + index + " falcon-512 key is not 897 bytes"); + } + return Arrays.copyOfRange(k.publicKey(), 1, FALCON_512_PK_LENGTH); + } + return k.publicKey(); + } + + private static boolean sameKeys(final Row a, final Row b) { + if (a.keys().size() != b.keys().size()) { + return false; + } + for (final Map.Entry e : a.keys().entrySet()) { + final Key other = b.keys().get(e.getKey()); + if (other == null || !Arrays.equals(other.publicKey(), e.getValue().publicKey())) { + return false; + } + } + return true; + } + + private static Address recover(final Bytes32 digest, final byte[] signature) { + if (signature == null || signature.length != ECDSA_SIGNATURE_LENGTH) { + return null; + } + try { + final SECPSignature s = + SignatureAlgorithmFactory.getInstance().decodeSignature(Bytes.wrap(signature)); + return Util.signatureToAddress(s, Hash.wrap(digest)); + } catch (final RuntimeException ex) { + return null; + } + } + + private static byte[] parseHex( + final JsonNode n, final int len, final Code code, final String what) { + if (n == null || !n.isTextual()) { + throw new RegistryV3Exception(code, what + " must be a 0x-prefixed hex string"); + } + final String s = n.asText(); + if (!s.startsWith("0x") || (s.length() % 2) != 0 || !s.substring(2).matches("[0-9a-f]*")) { + throw new RegistryV3Exception(code, what + " is not lowercase 0x-hex of even length"); + } + final byte[] b = Bytes.fromHexString(s).toArrayUnsafe(); + if (len >= 0 && b.length != len) { + throw new RegistryV3Exception(code, what + " is " + b.length + " bytes, expected " + len); + } + return b; + } + + private static Bytes32 parseBytes32(final JsonNode n, final String what) { + return Bytes32.wrap(parseHex(n, 32, Code.REG3_16_HASH, what)); + } + + private static Bytes32 node(final Bytes32 l, final Bytes32 r) { + final byte[] in = new byte[65]; + in[0] = MERKLE_NODE_TAG; + System.arraycopy(l.toArrayUnsafe(), 0, in, 1, 32); + System.arraycopy(r.toArrayUnsafe(), 0, in, 33, 32); + return Bytes32.wrap(keccak(in)); + } + + private static void writeAll(final ByteArrayOutputStream out, final byte[] b) { + out.write(b, 0, b.length); + } + + private static byte[] uint32be(final int v) { + return new byte[] {(byte) (v >>> 24), (byte) (v >>> 16), (byte) (v >>> 8), (byte) v}; + } + + private static byte[] uint64be(final long v) { + final byte[] b = new byte[8]; + for (int i = 0; i < 8; i++) { + b[i] = (byte) (v >>> (56 - 8 * i)); + } + return b; + } + + private static byte[] keccak(final byte[] input) { + final KeccakDigest d = new KeccakDigest(256); + d.update(input, 0, input.length); + final byte[] out = new byte[32]; + d.doFinal(out, 0); + return out; + } + + private static String hex(final byte[] b) { + final StringBuilder sb = new StringBuilder(b.length * 2); + for (final byte x : b) { + sb.append(String.format(Locale.ROOT, "%02x", x & 0xff)); + } + return sb.toString(); + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSchemeSchedule.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSchemeSchedule.java index a4d1bd43e3..6a706ed712 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSchemeSchedule.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSchemeSchedule.java @@ -1,135 +1,245 @@ /* * AERE crypto-agility, step 5: the height-indexed scheme schedule. * * WHAT IT IS. The same shape as aere.pq.anchorMinSeals ("H:K,H:K,..."), but the value at each * step is a SET of scheme ids: "14000000:falcon-512,15500000:falcon-512+slh-dsa-128s" reads * "from 14,000,000 anchors carry Falcon; from 15,500,000 they carry Falcon AND SLH-DSA". * Changing the mathematics of the chain becomes one property plus keys, never a code edit - * that is the whole point of the abstraction layer. * + * PER-SCHEME INTERVALS (AIP-22 stage 2, 2026-09-23). A scheme in a step may carry its own anchor + * interval, "scheme/interval": "H:falcon-512/32+slh-dsa-sha2-128s/128" reads "from H, Falcon-512 + * is required at every anchor height on the 32 grid and SLH-DSA-SHA2-128s only at the anchor + * heights on the 128 grid", both grids counted from the chain's anchor activation height H0 (the + * one every anchor grid of this chain is counted from, so an anchor of the coarse grid is always + * an anchor of the fine one). A scheme without "/interval" is required at EVERY anchor height, + * which is exactly the behaviour before this change, so an existing schedule keeps its meaning + * byte for byte. The intervals are validated as positive multiples of the anchor grid unit; the + * cross-check against the interval the node actually runs is done where both are known + * (HybridSealSupport at load), never assumed here. + * * THE D-147 LESSON, APPLIED AT THE LOADER. The min-seals schedule once accepted a shape whose * DANGEROUS step was later in the schedule, because validation looked only at the first step. * Here every rule runs over the WHOLE schedule at parse time, and the armability gate * (firstUnsatisfied) walks every step against the registry's per-scheme coverage: arming a * threshold K under a scheme whose coverage is below K is a chain stop, so it must be refused * at configuration time, loudly, before any node boots with it. * * SEMANTICS OF "BEFORE THE FIRST STEP": schemesAt returns the empty set, which callers read as * "the v2 scheme world is not armed here" (the legacy untagged Falcon certificate governs). * Empty is never a default INSIDE the schedule: a step with zero schemes is a parse refusal. */ package org.hyperledger.besu.consensus.common.bft; import com.google.common.base.Splitter; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; /** Immutable, validated height-to-scheme-set schedule. */ public final class PqSchemeSchedule { - /** One step: from {@code fromBlock} (inclusive) the anchor carries {@code schemeIds}. */ - public record Step(long fromBlock, Set schemeIds) {} + /** + * The unit every anchor grid of an Aere chain is a multiple of (the base anchor interval of both + * chain 2800 and testnet 28001). A per-scheme interval that is not a multiple of it could never + * land on anchor heights, so it is refused at parse time. + */ + public static final int GRID_UNIT = 32; + + /** + * One step: from {@code fromBlock} (inclusive) the anchor carries {@code schemeIds}; a scheme + * present in {@code intervals} is required only at anchor heights on that interval's grid, + * a scheme absent from it at every anchor height. + */ + public record Step(long fromBlock, Set schemeIds, Map intervals) { + /** Canonical form: defensive copies, never null. */ + public Step( + final long fromBlock, final Set schemeIds, final Map intervals) { + this.fromBlock = fromBlock; + this.schemeIds = Set.copyOf(schemeIds); + this.intervals = intervals == null ? Map.of() : Map.copyOf(intervals); + } + + /** A step whose schemes are all required at every anchor height (the pre-AIP-22 form). */ + public Step(final long fromBlock, final Set schemeIds) { + this(fromBlock, schemeIds, Map.of()); + } + } private final List steps; private PqSchemeSchedule(final List steps) { this.steps = steps; } - /** Parse "H:scheme[+scheme...],H:...". Refuses the WHOLE schedule on any defect: unknown or - * repeated scheme in a step, empty step, non-increasing heights, negative height, garbage. */ + /** Parse "H:scheme[/interval][+scheme[/interval]...],H:...". Refuses the WHOLE schedule on any + * defect: unknown or repeated scheme in a step, empty step, non-increasing heights, negative + * height, an interval that is not a positive multiple of {@link #GRID_UNIT}, garbage. */ public static PqSchemeSchedule parse(final String raw) { if (raw == null || raw.isBlank()) { throw new IllegalArgumentException("AERE PQ ORAR-SCHEME: empty schedule"); } final List steps = new ArrayList<>(); long lastHeight = -1; for (final String piesa : Splitter.on(',').split(raw)) { final List parti = Splitter.on(':').splitToList(piesa.trim()); if (parti.size() != 2) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: step '" + piesa.trim() + "' is not H:schemes"); } final long h; try { h = Long.parseLong(parti.get(0).trim()); } catch (final NumberFormatException e) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: height '" + parti.get(0).trim() + "' is not a number"); } if (h < 0) { throw new IllegalArgumentException("AERE PQ ORAR-SCHEME: negative height " + h); } if (h <= lastHeight) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: heights must strictly increase (" + lastHeight + " then " + h + ") - a schedule read out of order would arm the wrong mathematics"); } lastHeight = h; final Set schemes = new LinkedHashSet<>(); + final Map intervals = new LinkedHashMap<>(); for (final String id : Splitter.on('+').split(parti.get(1))) { - final String curat = id.trim(); - if (curat.isEmpty()) { + final String piece = id.trim(); + if (piece.isEmpty()) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: step at " + h + " carries an empty scheme name"); } + final int slash = piece.indexOf('/'); + final String curat = slash < 0 ? piece : piece.substring(0, slash).trim(); if (SealSchemes.byId(curat).isEmpty()) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: step at " + h + " names UNKNOWN scheme '" + curat + "' - refusing the whole schedule"); } if (!schemes.add(curat)) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: step at " + h + " repeats scheme '" + curat + "'"); } + if (slash >= 0) { + final String rawInterval = piece.substring(slash + 1).trim(); + final int interval; + try { + interval = Integer.parseInt(rawInterval); + } catch (final NumberFormatException e) { + throw new IllegalArgumentException( + "AERE PQ ORAR-SCHEME: step at " + h + " gives scheme '" + curat + + "' the interval '" + rawInterval + "', which is not a number"); + } + if (interval <= 0 || interval % GRID_UNIT != 0) { + throw new IllegalArgumentException( + "AERE PQ ORAR-SCHEME: step at " + h + " gives scheme '" + curat + + "' the interval " + interval + ", which is not a positive multiple of the" + + " anchor grid unit " + GRID_UNIT + " - such a scheme could never land on an" + + " anchor height"); + } + intervals.put(curat, interval); + } } if (schemes.isEmpty()) { throw new IllegalArgumentException( "AERE PQ ORAR-SCHEME: step at " + h + " has no schemes at all"); } - steps.add(new Step(h, Set.copyOf(schemes))); + steps.add(new Step(h, schemes, intervals)); } return new PqSchemeSchedule(List.copyOf(steps)); } - /** The scheme set in force at {@code height}: the last step at or below it, or the empty set - * when the schedule has not started yet (= v2 not armed, legacy governs). */ - public Set schemesAt(final long height) { - Set inForce = Set.of(); + /** The step in force at {@code height}, or null before the first one. */ + private Step stepAt(final long height) { + Step inForce = null; for (final Step s : steps) { if (s.fromBlock() <= height) { - inForce = s.schemeIds(); + inForce = s; } else { break; } } return inForce; } + /** The scheme set in force at {@code height}: the last step at or below it, or the empty set + * when the schedule has not started yet (= v2 not armed, legacy governs). This is the set a + * node must hold KEYS for and the set the armability gate checks; which of them an anchor + * actually demands is {@link #schemesRequiredForAnchor}. */ + public Set schemesAt(final long height) { + final Step s = stepAt(height); + return s == null ? Set.of() : s.schemeIds(); + } + + /** + * The schemes an anchor at {@code anchorHeight} must carry, given that its scheme step is read + * at the PARENT height (the seals of a certificate are made when the parent is committed, so + * every consumer of this schedule already indexes it by the parent), and that a scheme with a + * per-scheme interval is required only when {@code (anchorHeight - anchorBlock)} is a multiple + * of that interval. Without per-scheme intervals this is exactly {@code schemesAt(anchorHeight + * - 1)}. + * + * @param anchorHeight the anchor height being produced, enforced or validated + * @param anchorBlock the chain's anchor activation height, the origin of every anchor grid + * @return the required scheme ids, possibly a strict subset of the step's schemes + */ + public Set schemesRequiredForAnchor(final long anchorHeight, final long anchorBlock) { + final Step s = stepAt(anchorHeight - 1); + if (s == null) { + return Set.of(); + } + if (s.intervals().isEmpty()) { + return s.schemeIds(); + } + final Set required = new LinkedHashSet<>(); + for (final String id : s.schemeIds()) { + final Integer interval = s.intervals().get(id); + if (interval == null || onGrid(anchorHeight, anchorBlock, interval)) { + required.add(id); + } + } + return Set.copyOf(required); + } + + private static boolean onGrid(final long height, final long origin, final int interval) { + if (height < origin) { + return false; + } + return (height - origin) % interval == 0; + } + + /** Whether any step carries a per-scheme interval (then the grid cross-check applies). */ + public boolean hasIntervals() { + return steps.stream().anyMatch(s -> !s.intervals().isEmpty()); + } + /** All steps, ascending. */ public List steps() { return steps; } /** The armability gate: walk EVERY step and every scheme in it against the registry's * per-scheme coverage; the first (height, scheme) whose coverage is below {@code minSeals} * is returned as the refusal, with numbers. Empty means the whole schedule is armable. * This is the D-147 discipline: the dangerous step may be the LAST one, so all are walked. */ public Optional firstUnsatisfied(final HybridSignerRegistry registry, final int minSeals) { for (final Step s : steps) { for (final String scheme : s.schemeIds()) { final int acoperire = registry.coverage(scheme); if (acoperire < minSeals) { return Optional.of( "step at height " + s.fromBlock() + " arms scheme '" + scheme + "' with required seals " + minSeals + " but the registry covers only " + acoperire + " validator(s) - arming this would stop the chain"); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSealed.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSealed.java new file mode 100644 index 0000000000..34049da6fc --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/PqSealed.java @@ -0,0 +1,61 @@ +/* + * 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; + +import org.hyperledger.besu.datatypes.Address; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes32; + +/** + * AERE AIP-22 stage 4: a consensus payload that carries a post-quantum seal {@code (index, signature)}. + * The four QBFT payloads (proposal, prepare, commit, round-change) implement it; from H_fin the author + * of a message is the id of its seal's index, and only once the seal is VERIFIED + * ({@link AerePqFinality#authorFromSeal}): the author decides whether a message is even relayed, which + * happens before the layers judge it. + */ +public interface PqSealed { + + /** + * The post-quantum seal this payload carries, if any. + * + * @return the seal + */ + Optional getFalconSeal(); + + /** + * The message this payload's seal signs over a chain, as the seal layer that judges it builds it + * (PREPARE, PROPOSAL and ROUND-CHANGE carry every field of it). Empty when the payload alone does not + * determine it (a COMMIT's seal signs the block's on-chain hash). + * + * @param chainId the chain id that goes into the message + * @return the message, or empty + */ + default Optional pqSealMessage(final long chainId) { + return Optional.empty(); + } + + /** + * For a COMMIT, the address that signed its commit seal; empty for every other payload. From H_fin a + * COMMIT's author is its seal index's id only when that is this signer's id (the binding its seal + * layer checks), and the seal itself is judged by the commit layer, armed at or below H_fin. + * + * @return the commit seal's signer, or empty + */ + default Optional

    commitSealSigner() { + return Optional.empty(); + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java index 82d86d5359..e43afaca83 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreator.java @@ -1,57 +1,65 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-prevrandao-block-creation.patch. */ package org.hyperledger.besu.consensus.common.bft.blockcreation; import org.hyperledger.besu.config.BftConfigOptions; import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions; import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec; import org.hyperledger.besu.consensus.common.bft.BftHelpers; import org.hyperledger.besu.consensus.common.bft.BftProtocolSchedule; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.SealableBlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import java.util.Collections; import java.util.Optional; +import org.apache.tuweni.bytes.Bytes32; + /** The Bft block creator. */ // This class is responsible for creating a block without committer seals (basically it was just // too hard to coordinate with the state machine). public class BftBlockCreator extends AbstractBlockCreator { private final BftExtraDataCodec bftExtraDataCodec; /** * Instantiates a new Bft block creator. * * @param miningConfiguration the mining parameters * @param forksSchedule the forks schedule * @param localAddress the local address * @param extraDataCalculator the extra data calculator * @param transactionPool the pending transactions * @param protocolContext the protocol context * @param protocolSchedule the protocol schedule * @param bftExtraDataCodec the bft extra data codec * @param ethScheduler the scheduler for asynchronous block creation tasks */ @@ -68,67 +76,80 @@ public class BftBlockCreator extends AbstractBlockCreator { super( miningConfiguration.setCoinbase(localAddress), miningBeneficiaryCalculator(protocolSchedule), extraDataCalculator, transactionPool, protocolContext, protocolSchedule, ethScheduler); this.bftExtraDataCodec = bftExtraDataCodec; } @Override public BlockCreationResult createBlock(final long timestamp, final BlockHeader parentHeader) { ProtocolSpec protocolSpec = ((BftProtocolSchedule) protocolSchedule) .getByBlockNumberOrTimestamp(parentHeader.getNumber() + 1, timestamp); if (protocolSpec.getWithdrawalsProcessor().isPresent()) { return createEmptyWithdrawalsBlock(timestamp, parentHeader); } else { - return createBlock(Optional.empty(), Optional.empty(), timestamp, parentHeader); + return createBlock( + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(Bytes32.wrap(BftHelpers.EXPECTED_MIX_HASH.getBytes())), + Optional.empty(), + Optional.empty(), + timestamp, + true, + parentHeader); } } /** * Construct a mining beneficiary calculator using the given protocolSchedule * * @param protocolSchedule protocol schedule * @return mining beneficiary calculator */ public static MiningBeneficiaryCalculator miningBeneficiaryCalculator( final ProtocolSchedule protocolSchedule) { return (blockTimestamp, pendingHeader) -> { BlockHeader newBlockHeader = BlockHeaderBuilder.createDefault() .coinbase(pendingHeader.getCoinbase()) .buildBlockHeader(); ProtocolSpec protocolSpec = ((BftProtocolSchedule) protocolSchedule) .getByBlockNumberOrTimestamp(pendingHeader.getNumber(), blockTimestamp); return protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(newBlockHeader); }; } @Override public BlockCreationResult createEmptyWithdrawalsBlock( final long timestamp, final BlockHeader parentHeader) { return createBlock( Optional.empty(), Optional.empty(), Optional.of(Collections.emptyList()), + Optional.of(Bytes32.wrap(BftHelpers.EXPECTED_MIX_HASH.getBytes())), + Optional.empty(), + Optional.empty(), timestamp, + true, parentHeader); } @Override protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) { final BlockHeaderBuilder builder = BlockHeaderBuilder.create() .populateFrom(sealableBlockHeader) .mixHash(BftHelpers.EXPECTED_MIX_HASH) .nonce(0L) .blockHeaderFunctions(BftBlockHeaderFunctions.forCommittedSeal(bftExtraDataCodec)); return builder.buildBlockHeader(); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java index 1ecdcbf34e..cefa0a039f 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftBlockCreatorFactory.java @@ -6,40 +6,41 @@ * * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.common.bft.blockcreation; import static com.google.common.base.Preconditions.checkState; import org.hyperledger.besu.config.BftConfigOptions; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.ConsensusHelpers; import org.hyperledger.besu.consensus.common.ForksSchedule; import org.hyperledger.besu.consensus.common.bft.BftContext; import org.hyperledger.besu.consensus.common.bft.BftExtraData; import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec; import org.hyperledger.besu.consensus.common.bft.Vote; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.consensus.common.validator.ValidatorVote; import org.hyperledger.besu.consensus.common.validator.VoteProvider; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.blockcreation.BlockCreator; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -102,41 +103,42 @@ public class BftBlockCreatorFactory { this.transactionPool = transactionPool; this.protocolContext = protocolContext; this.protocolSchedule = protocolSchedule; this.forksSchedule = forksSchedule; this.localAddress = localAddress; this.miningConfiguration = miningParams; this.bftExtraDataCodec = bftExtraDataCodec; this.ethScheduler = ethScheduler; } /** * Create block creator. * * @param round the round * @return the block creator */ public BlockCreator create(final int round) { return new BftBlockCreator( miningConfiguration, forksSchedule, - localAddress, + AerePqIdentity.local( + localAddress, protocolContext.getBlockchain().getChainHeadBlockNumber() + 1L), ph -> createExtraData(round, ph), transactionPool, protocolContext, protocolSchedule, bftExtraDataCodec, ethScheduler); } /** * Sets min transaction gas price. * * @param minTransactionGasPrice the min transaction gas price */ public void setMinTransactionGasPrice(final Wei minTransactionGasPrice) { miningConfiguration.setMinTransactionGasPrice(minTransactionGasPrice); } /** * Gets min transaction gas price. * @@ -169,41 +171,44 @@ public class BftBlockCreatorFactory { /** * Build the extra data VALUE for a block on this parent, before it is encoded. * *

    AERE ANCHOR V2: split out of {@link #createExtraData(int, BlockHeader)} with NO behaviour * change, so a subclass can post-process the value (QBFT rewrites vanityData with the anchor * digest and fills element 6 with the parent's certificate) without decoding and * re-encoding bytes it just produced. IBFT, which shares this class, keeps the old path exactly: * it never overrides this and never sees the anchor. * * @param round the round * @param parentHeader the parent header * @return the extra data value */ protected BftExtraData buildExtraData(final int round, final BlockHeader parentHeader) { final BftContext bftContext = protocolContext.getConsensusContext(BftContext.class); final ValidatorProvider validatorProvider = bftContext.getValidatorProvider(); Optional voteProviderAfterBlock = validatorProvider.getVoteProviderAfterBlock(parentHeader); checkState(voteProviderAfterBlock.isPresent(), "Bft requires a vote provider"); final Optional proposal = - voteProviderAfterBlock.get().getVoteAfterBlock(parentHeader, localAddress); + voteProviderAfterBlock + .get() + .getVoteAfterBlock( + parentHeader, AerePqIdentity.local(localAddress, parentHeader.getNumber() + 1L)); final List

    validators = new ArrayList<>(validatorProvider.getValidatorsAfterBlock(parentHeader)); final BftExtraData extraData = new BftExtraData( ConsensusHelpers.zeroLeftPad( miningConfiguration.getExtraData(), BftExtraDataCodec.EXTRA_VANITY_LENGTH), Collections.emptyList(), toVote(proposal), round, validators); return extraData; } /** * Change target gas limit. * * @param newTargetGasLimit the new target gas limit diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftMiningCoordinator.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftMiningCoordinator.java index 25474631c8..da4eeecd8c 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftMiningCoordinator.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/BftMiningCoordinator.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-coordinator-lifecycle-sync.patch. */ package org.hyperledger.besu.consensus.common.bft.blockcreation; import org.hyperledger.besu.consensus.common.bft.BftEventQueue; import org.hyperledger.besu.consensus.common.bft.BftExecutors; import org.hyperledger.besu.consensus.common.bft.BftProcessor; import org.hyperledger.besu.consensus.common.bft.events.NewChainHead; import org.hyperledger.besu.consensus.common.bft.statemachine.BftEventHandler; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; import org.hyperledger.besu.ethereum.chain.BlockAddedObserver; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.plugin.services.BesuEvents; @@ -177,52 +183,56 @@ public class BftMiningCoordinator implements MiningCoordinator, BlockAddedObserv public void onInitialSyncCompleted() { LOG.info("Starting BFT mining coordinator following initial sync"); enable(); start(); } @Override public void onInitialSyncRestart() { // Nothing to do. The mining coordinator won't be started until // sync has completed. } }); } @Override public void awaitStop() throws InterruptedException { bftExecutors.awaitStop(); } @Override - public boolean enable() { + public synchronized boolean enable() { + // AERE D-434 (81a600236a, issue #10877): synchronized with start()/stop() so the multi-step + // state checks below cannot interleave with a concurrent lifecycle transition. // Return true if we're already running or idle, or successfully switch to idle if (state.get() == State.RUNNING || state.get() == State.IDLE || state.compareAndSet(State.PAUSED, State.IDLE)) { return true; } return false; } @Override - public boolean disable() { + public synchronized boolean disable() { + // AERE D-434 (81a600236a, issue #10877): synchronized with start()/stop() so the multi-step + // state checks below cannot interleave with a concurrent lifecycle transition. if (state.get() == State.PAUSED || state.compareAndSet(State.IDLE, State.PAUSED) || state.compareAndSet(State.RUNNING, State.PAUSED)) { return true; } return false; } @Override public boolean isMining() { return state.get() == State.RUNNING; } @Override public Wei getMinTransactionGasPrice() { return blockCreatorFactory.getMinTransactionGasPrice(); } @Override public Wei getMinPriorityFeePerGas() { diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/PqAnchorProducer.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/PqAnchorProducer.java index a46b7d8874..e8fc683458 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/PqAnchorProducer.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/blockcreation/PqAnchorProducer.java @@ -1,38 +1,39 @@ /* * 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.blockcreation; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.BftContext; import org.hyperledger.besu.consensus.common.bft.BftExtraData; import org.hyperledger.besu.consensus.common.bft.FalconSeal; import org.hyperledger.besu.consensus.common.bft.FalconSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSignerRegistry; import org.hyperledger.besu.consensus.common.bft.PqAnchor; import org.hyperledger.besu.consensus.common.bft.PqAnchorConfig; import org.hyperledger.besu.consensus.common.bft.PqAnchorNotReadyException; import org.hyperledger.besu.consensus.common.bft.PqAnchorV2; import org.hyperledger.besu.consensus.common.bft.PqSealCache; 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.datatypes.Address; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; import java.util.ArrayList; @@ -363,75 +364,86 @@ public final class PqAnchorProducer { * Falcon. vanityData becomes the v2 digest under "AERE-PQ-ANCHOR-2". Refuses, through the same * not-ready exception the v1 path uses, rather than writing a certificate short of a scheme. */ private static BftExtraData applyV2( final PqAnchorConfig cfg, final long blockNumber, final BlockHeader parentHeader, final BftExtraData base, final List falconCertificate, final Bytes32 message, final int required, final int sealCap) { final long parentNumber = parentHeader.getNumber(); final List v2 = new ArrayList<>(); final Set falconIndices = new LinkedHashSet<>(); for (final FalconSeal f : falconCertificate) { v2.add(new SchemeSeal(SealSchemes.FALCON_512.wireId(), f.getValidatorIndex(), f.getSignature())); falconIndices.add(f.getValidatorIndex()); } final HybridSealSupport hybrid = HybridSealSupport.instance(); + // AIP-22 stage 2: only the schemes THIS anchor height demands (per-scheme grids counted from + // the activation height); at a Falcon-only anchor no SLH-DSA seal is gathered or written, which + // is the whole disk saving of the two-grid design final Set schemes = - hybrid.schedule().map(sch -> sch.schemesAt(parentNumber)).orElse(Set.of()); + hybrid + .schedule() + .map(sch -> sch.schemesRequiredForAnchor(blockNumber, cfg.anchorBlock())) + .orElse(Set.of()); final List heard = PqSealCache.instance().extrasFor(parentNumber, parentHeader.getHash()); for (final String schemeId : schemes) { if (SealSchemes.FALCON_512.id().equals(schemeId)) { continue; } final Optional scheme = SealSchemes.byId(schemeId); if (scheme.isEmpty()) { throw new PqAnchorNotReadyException( parentNumber, 0, required, "the scheme schedule names '" + schemeId + "', which this binary does not implement"); } final Optional registry = hybrid.registry(); - if (registry.isEmpty()) { + // AERE AIP-22 stage 3b: from H_k the keys come from the v3 epoch in force at the parent + final boolean v3Keys = AerePqIdentity.keysFromV3(parentNumber); + if (registry.isEmpty() && !v3Keys) { throw new PqAnchorNotReadyException( parentNumber, 0, required, "no hybrid registry is loaded on this node, so no " + schemeId + " seal can be verified"); } int kept = 0; int rejected = 0; for (final SchemeSeal s : heard) { if (s.getSchemeWireId() != scheme.get().wireId()) { continue; } if (!falconIndices.contains(s.getValidatorIndex())) { rejected++; continue; } - final Optional pk = registry.get().publicKey(s.getValidatorIndex(), schemeId); + final Optional pk = + v3Keys + ? AerePqIdentity.v3KeyAt(parentNumber, s.getValidatorIndex(), schemeId) + : registry.get().publicKey(s.getValidatorIndex(), schemeId); if (pk.isEmpty() || !scheme.get().verifyRaw(pk.get(), message.toArray(), s.getSignature().toArray())) { rejected++; continue; } v2.add(s); kept++; if (kept >= sealCap) { break; } } if (kept < required) { throw new PqAnchorNotReadyException( parentNumber, kept, required, "V2: heard " + heard.size() + " extra seal(s) for parent hash " + parentHeader.getHash() diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/messagewrappers/BftMessage.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/messagewrappers/BftMessage.java index 0a88b579fd..e52c52905e 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/messagewrappers/BftMessage.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/messagewrappers/BftMessage.java @@ -1,60 +1,72 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-bounded-message-lists.patch. */ package org.hyperledger.besu.consensus.common.bft.messagewrappers; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.payload.Authored; import org.hyperledger.besu.consensus.common.bft.payload.Payload; import org.hyperledger.besu.consensus.common.bft.payload.RoundSpecific; import org.hyperledger.besu.consensus.common.bft.payload.SignedData; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.util.Objects; import java.util.StringJoiner; import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; /** * The Bft message. * * @param

    the type of Payload */ public class BftMessage

    implements Authored, RoundSpecific { + /** + * Maximum number of entries permitted in any embedded list (round-changes, prepares) when + * decoding a BFT message. AERE D-434: ported from upstream hyperledger/besu dd4101c9ec. + */ + public static final int MAX_LIST_ENTRIES = 512; + private final SignedData

    payload; /** * Instantiates a new Bft message. * * @param payload the payload */ public BftMessage(final SignedData

    payload) { this.payload = payload; } @Override public Address getAuthor() { return payload.getAuthor(); } @Override public ConsensusRoundIdentifier getRoundIdentifier() { return payload.getPayload().getRoundIdentifier(); } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/network/ValidatorPeers.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/network/ValidatorPeers.java index f4fccecf3c..649fa75cf4 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/network/ValidatorPeers.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/network/ValidatorPeers.java @@ -1,36 +1,43 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.common.bft.network; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Responsible for tracking the network peers which have a connection to this node, then * multicasting packets to ONLY the peers which have been identified as being validators. @@ -76,41 +83,46 @@ public class ValidatorPeers implements ValidatorMulticaster, PeerConnectionTrack } @Override public void send(final MessageData message) { sendMessageToSpecificAddresses(getLatestValidators(), message); } @Override public void send(final MessageData message, final Collection

    denylist) { final Collection
    includedValidators = getLatestValidators().stream() .filter(a -> !denylist.contains(a)) .collect(Collectors.toSet()); sendMessageToSpecificAddresses(includedValidators, message); } private void sendMessageToSpecificAddresses( final Collection
    recipients, final MessageData message) { LOG.trace( "Sending message to peers messageCode={} recipients={}", message.getCode(), recipients); - recipients.stream() - .map(connectionsByAddress::get) - .filter(Objects::nonNull) + // AERE AIP-22 stage 3: from H_id the recipients are ids; they are reached through the node-key + // address their v3 row lists as transport, or through every peer when a row lists none + final java.util.Optional> targets = + AerePqIdentity.transportTargets(recipients); + (targets.isPresent() + ? targets.get().stream().map(connectionsByAddress::get).filter(Objects::nonNull) + : connectionsByAddress.values().stream()) .flatMap(Set::stream) + .distinct() .forEach( connection -> { try { connection.sendForProtocol(protocolName, message); } catch (final PeerNotConnected peerNotConnected) { LOG.trace( "Lost connection to a validator. remoteAddress={} peerInfo={}", connection.getRemoteAddress(), connection.getPeerInfo()); } }); } private Collection
    getLatestValidators() { return validatorProvider.getValidatorsAtHead(); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/payload/SignedData.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/payload/SignedData.java index 00326eec42..2c59e698b6 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/payload/SignedData.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/payload/SignedData.java @@ -1,77 +1,94 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.common.bft.payload; +import org.hyperledger.besu.consensus.common.bft.AerePqFinality; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import java.math.BigInteger; import java.util.Objects; import java.util.StringJoiner; import org.apache.tuweni.bytes.Bytes; /** * The type Signed data. * * @param the type parameter of Payload */ public class SignedData implements Authored { private static final BigInteger HALF_CURVE_ORDER = SignatureAlgorithmFactory.getInstance().getHalfCurveOrder(); private final Address sender; private final SECPSignature signature; private final M unsignedPayload; /** * Create signed data. * * @param the type parameter * @param payload the payload * @param signature the signature * @return the signed data * @throws IllegalArgumentException if the signature is invalid */ public static SignedData create( final T payload, final SECPSignature signature) { final Hash msgHash = payload.hashForSignature(); - return new SignedData<>(payload, Util.signatureToAddress(signature, msgHash), signature); + // AERE AIP-22 stage 3: from H_id the author is the post-quantum id of the signer's v3 row + // AERE AIP-22 stage 4: from H_fin the author is the id of the row the message's post-quantum seal index names, and the + // envelope's ECDSA signature is not read for authorship (a message without a post-quantum seal has no author then) + final long aereHeight = payload.getRoundIdentifier().getSequenceNumber(); + return new SignedData<>( + payload, + AerePqFinality.active(aereHeight) + ? AerePqFinality.authorFromSeal(payload, aereHeight) + : AerePqIdentity.author(Util.signatureToAddress(signature, msgHash), aereHeight), + signature); } private SignedData(final M unsignedPayload, final Address sender, final SECPSignature signature) { if (!(signature.getS().compareTo(HALF_CURVE_ORDER) <= 0)) { throw new IllegalArgumentException("Signature is invalid"); } this.unsignedPayload = unsignedPayload; this.sender = sender; this.signature = signature; } @Override public Address getAuthor() { return sender; } /** * Gets payload. * * @return the payload diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ADRS.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ADRS.java index 7610cf8762..ca87df83d3 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ADRS.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ADRS.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @SuppressWarnings("all") class ADRS { static final int WOTS_HASH = 0; static final int WOTS_PK = 1; static final int TREE = 2; static final int FORS_TREE = 3; static final int FORS_PK = 4; static final int WOTS_PRF = 5; static final int FORS_PRF = 6; static final int OFFSET_LAYER = 0; static final int OFFSET_TREE = 4; diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/Fors.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/Fors.java index d89d83af86..803fca98b4 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/Fors.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/Fors.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.math.BigInteger; import java.util.LinkedList; import org.bouncycastle.util.Arrays; @SuppressWarnings("all") class Fors { SLHDSAEngine engine; public Fors(SLHDSAEngine engine) { this.engine = engine; } // Input: Secret seed SK.seed, start index s, target node height z, public seed PK.seed, address ADRS diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/HT.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/HT.java index b7d96c16f8..88ff081efe 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/HT.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/HT.java @@ -1,46 +1,60 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.util.LinkedList; import org.bouncycastle.util.Arrays; @SuppressWarnings("all") class HT { private final byte[] skSeed; private final byte[] pkSeed; SLHDSAEngine engine; WotsPlus wots; final byte[] htPubKey; + // AERE (D-337, 2026-09-26): the same object without htPubKey. The constructor below computes the + // top-layer public key (a full treehash of 2^H' WOTS+ leaves) on every signature, and signing never + // reads it. Used by ParallelSigning; key generation keeps the constructor below. + HT(SLHDSAEngine engine, byte[] skSeed, byte[] pkSeed, boolean withoutPublicKey) + { + this.skSeed = skSeed; + this.pkSeed = pkSeed; + this.engine = engine; + this.wots = new WotsPlus(engine); + this.htPubKey = null; + } + public HT(SLHDSAEngine engine, byte[] skSeed, byte[] pkSeed) { this.skSeed = skSeed; this.pkSeed = pkSeed; this.engine = engine; this.wots = new WotsPlus(engine); ADRS adrs = new ADRS(); adrs.setLayerAddress(engine.D - 1); adrs.setTreeAddress(0); if (skSeed != null) { htPubKey = xmss_PKgen(skSeed, pkSeed, adrs); } else { htPubKey = null; } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/IndexedDigest.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/IndexedDigest.java index 8ff076d70d..49bf358925 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/IndexedDigest.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/IndexedDigest.java @@ -1,26 +1,28 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class IndexedDigest { final long idx_tree; final int idx_leaf; final byte[] digest; IndexedDigest(long idx_tree, int idx_leaf, byte[] digest) { this.idx_tree = idx_tree; this.idx_leaf = idx_leaf; this.digest = digest; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/JdkDigest.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/JdkDigest.java index c7a8e86fae..7edcab834a 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/JdkDigest.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/JdkDigest.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.util.Memoable; /** * A Bouncy Castle {@link ExtendedDigest} backed by the JDK's {@link MessageDigest}, so SLH-DSA's * millions of short hashes run on the SHA-NI intrinsics. Measured 2026-09-04 on an EPYC host: 3M * SHA-256 of 64 bytes, Bouncy Castle 2533 ms, JDK 335 ms; one sha2_128s signature 1364 ms before, * 325 ms after. {@link Memoable} is how the SLH-DSA engine keeps the pk-seed prefix state: it is * implemented here with {@link MessageDigest#clone()}, which the SUN provider supports for SHA-2. */ @SuppressWarnings("all") final class JdkDigest implements ExtendedDigest, Memoable { private final String algorithm; diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/NodeEntry.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/NodeEntry.java index 4cd30cfff7..adaa9abe3e 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/NodeEntry.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/NodeEntry.java @@ -1,24 +1,26 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class NodeEntry { final byte[] nodeValue; final int nodeHeight; NodeEntry(byte[] nodeValue, int nodeHeight) { this.nodeValue = nodeValue; this.nodeHeight = nodeHeight; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/PK.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/PK.java index 4d0ef5fe2b..8e4b32ee25 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/PK.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/PK.java @@ -1,24 +1,26 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class PK { final byte[] seed; final byte[] root; PK(byte[] seed, byte[] root) { this.seed = seed; this.root = root; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ParallelSigning.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ParallelSigning.java new file mode 100755 index 0000000000..2a1505ddcb --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/ParallelSigning.java @@ -0,0 +1,257 @@ +/* + * 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 + * + * Written by AERE Network (not derived from Bouncy Castle); emitted into this package by + * consensus-pqc/slhdsa-fork/genereaza-motorul.py from ParallelSigning.java.sablon. DO NOT EDIT BY HAND. + */ +package org.hyperledger.besu.consensus.common.bft.slhdsa; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; + +import org.bouncycastle.util.Arrays; + +/** + * SLH-DSA signing with the message-independent work spread over the cores; the SAME bytes as the + * sequential signer, which remains the fallback. + * + *

    D-337, measured 2026-09-26 on chain 2800: the parent of every anchor took 1.69 s against 0.60 s + * for an ordinary block (import times on two validators, fifteen anchors each), because each + * validator signs its Commit with SLH-DSA-SHA2-128s there, on the QBFT thread, on one core. One + * signature on a validator's CPU (EPYC-Rome, one core): 0.47 s warm, 0.88-1.02 s cold, and a + * validator signs once every ~77 s. Almost all of it is message-independent: the authentication + * path of each of the D hypertree layers (2^H' - 1 WOTS+ leaves each) and the k FORS trees. Only + * the WOTS+ signature of each layer depends on the root of the layer below, and that chain is cheap. + * So the auth paths and FORS run as separate tasks on a small daemon pool, each with its own engine + * (the engines hold digest state and are not thread-safe), and the chain is then walked in order. + * + *

    Also dropped: the HT constructor used by the sequential signer computes the top layer's public + * key (a full 2^H'-leaf treehash) on every signature, and signing never reads it. + * + *

    Taken only with more than one core and unless {@code -Daere.pq.slhdsa.parallel=false}; any + * failure returns null and the caller runs the original Bouncy Castle code unchanged. + */ +@SuppressWarnings("all") +final class ParallelSigning { + + static final String SWITCH_PROPERTY = "aere.pq.slhdsa.parallel"; + + private static final int THREADS = + Math.max(1, Math.min(8, Runtime.getRuntime().availableProcessors())); + + // counts tasks executed on the pool; the proof reads it to show the parallel path was the one taken + private static final AtomicInteger TASKS_RUN = new AtomicInteger(); + + private static volatile ExecutorService pool; + + private ParallelSigning() {} + + static boolean enabled() { + return THREADS > 1 + && !"false".equalsIgnoreCase(System.getProperty(SWITCH_PROPERTY, "true").trim()); + } + + static int tasksRun() { + return TASKS_RUN.get(); + } + + static int tasksPerSignature(final SLHDSAParameters params) { + return 1 + 2 * params.getEngine().D; + } + + private static ExecutorService pool() { + ExecutorService p = pool; + if (p == null) { + synchronized (ParallelSigning.class) { + p = pool; + if (p == null) { + final AtomicInteger n = new AtomicInteger(); + p = + Executors.newFixedThreadPool( + THREADS, + r -> { + final Thread t = new Thread(r, "aere-slhdsa-sign-" + n.incrementAndGet()); + t.setDaemon(true); + return t; + }); + pool = p; + } + } + } + return p; + } + + /** The signature, or null when this path is not taken; the caller then runs the original code. */ + static byte[] trySign( + final SLHDSAPrivateKeyParameters privKey, + final byte[] msgPrefix, + final byte[] msg, + final byte[] optRand) { + if (!enabled()) { + return null; + } + try { + return sign(privKey, msgPrefix, msg, optRand); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + return null; + } catch (final ExecutionException | RuntimeException e) { + return null; + } + } + + static byte[] sign( + final SLHDSAPrivateKeyParameters privKey, + final byte[] msgPrefix, + final byte[] msg, + final byte[] optRand) + throws InterruptedException, ExecutionException { + final SLHDSAParameters params = privKey.getParameters(); + final byte[] skSeed = privKey.sk.seed; + final byte[] pkSeed = privKey.pk.seed; + final SLHDSAEngine engine = params.getEngine(); + engine.init(pkSeed); + + // the randomiser and the message digest, exactly as SLHDSASigner.internalGenerateSignature + final byte[] r = engine.PRF_msg(privKey.sk.prf, optRand, msgPrefix, msg); + final IndexedDigest idxDigest = engine.H_msg(r, pkSeed, privKey.pk.root, msgPrefix, msg); + final byte[] mHash = idxDigest.digest; + final long idxTree = idxDigest.idx_tree; + final int idxLeaf = idxDigest.idx_leaf; + final int d = engine.D; + final int hPrime = engine.H_PRIME; + + // the (tree, leaf) each layer signs with: the walk HT.sign does, layer 0 first + final long[] tree = new long[d]; + final int[] leaf = new int[d]; + long t = idxTree; + int l = idxLeaf; + for (int j = 0; j < d; j++) { + if (j > 0) { + l = (int) (t & ((1 << hPrime) - 1)); + t >>>= hPrime; + } + tree[j] = t; + leaf[j] = l; + } + + final List> tasks = new ArrayList>(); + tasks.add(forsTask(params, skSeed, pkSeed, mHash, idxTree, idxLeaf)); + final byte[][][] auth = new byte[d][hPrime][]; + for (int j = 0; j < d; j++) { + // two halves per layer: the top auth node alone is half of the tree's leaves, the rest the other half + tasks.add(authTask(params, skSeed, pkSeed, j, tree[j], leaf[j], hPrime - 1, hPrime, auth)); + tasks.add(authTask(params, skSeed, pkSeed, j, tree[j], leaf[j], 0, hPrime - 1, auth)); + } + final List> done = pool().invokeAll(tasks); + final Object[] fors = (Object[]) done.get(0).get(); + for (int i = 1; i < done.size(); i++) { + done.get(i).get(); // surfaces a failed task, which sends the caller to the original code + } + for (int j = 0; j < d; j++) { + for (int h = 0; h < hPrime; h++) { + if (auth[j][h] == null) { + throw new IllegalStateException("auth path of layer " + j + " height " + h + " missing"); + } + } + } + final SIG_FORS[] sigFors = (SIG_FORS[]) fors[0]; + final byte[] pkFors = (byte[]) fors[1]; + + // the hypertree chain, in order: each layer's WOTS+ signs the root of the layer below (FORS for layer 0) + final HT ht = new HT(engine, skSeed, pkSeed, true); + byte[] m = pkFors; + final byte[][] htParts = new byte[d][]; + for (int j = 0; j < d; j++) { + final ADRS adrs = new ADRS(); + adrs.setLayerAddress(j); + adrs.setTreeAddress(tree[j]); + final ADRS wotsAdrs = new ADRS(adrs); + wotsAdrs.setTypeAndClear(ADRS.WOTS_HASH); + wotsAdrs.setKeyPairAddress(leaf[j]); + final SIG_XMSS xmss = new SIG_XMSS(ht.wots.sign(m, skSeed, pkSeed, wotsAdrs), auth[j]); + htParts[j] = Arrays.concatenate(xmss.sig, Arrays.concatenate(xmss.auth)); + if (j < d - 1) { + m = ht.xmss_pkFromSig(leaf[j], xmss, m, pkSeed, adrs); + } + } + + final byte[][] parts = new byte[sigFors.length + 2][]; + parts[0] = r; + for (int i = 0; i != sigFors.length; i++) { + parts[1 + i] = Arrays.concatenate(sigFors[i].sk, Arrays.concatenate(sigFors[i].authPath)); + } + parts[parts.length - 1] = Arrays.concatenate(htParts); + return Arrays.concatenate(parts); + } + + private static Callable forsTask( + final SLHDSAParameters params, + final byte[] skSeed, + final byte[] pkSeed, + final byte[] mHash, + final long idxTree, + final int idxLeaf) { + return () -> { + TASKS_RUN.incrementAndGet(); + final SLHDSAEngine e = params.getEngine(); + e.init(pkSeed); + final Fors fors = new Fors(e); + ADRS adrs = new ADRS(); + adrs.setTypeAndClear(ADRS.FORS_TREE); + adrs.setTreeAddress(idxTree); + adrs.setKeyPairAddress(idxLeaf); + final SIG_FORS[] sigFors = fors.sign(mHash, skSeed, pkSeed, adrs); + adrs = new ADRS(); + adrs.setTypeAndClear(ADRS.FORS_TREE); + adrs.setTreeAddress(idxTree); + adrs.setKeyPairAddress(idxLeaf); + return new Object[] {sigFors, fors.pkFromSig(sigFors, mHash, pkSeed, adrs)}; + }; + } + + // the auth-path loop of HT.xmss_sign, for heights [fromHeight, toHeight) of one layer's tree + private static Callable authTask( + final SLHDSAParameters params, + final byte[] skSeed, + final byte[] pkSeed, + final int layer, + final long treeAddress, + final int idx, + final int fromHeight, + final int toHeight, + final byte[][][] auth) { + return () -> { + TASKS_RUN.incrementAndGet(); + final SLHDSAEngine e = params.getEngine(); + e.init(pkSeed); + final HT ht = new HT(e, skSeed, pkSeed, true); + final ADRS a = new ADRS(); + a.setTypeAndClear(ADRS.TREE); + a.setLayerAddress(layer); + a.setTreeAddress(treeAddress); + for (int j = fromHeight; j < toHeight; j++) { + final int k = (idx >>> j) ^ 1; + auth[layer][j] = ht.treehash(skSeed, k << j, j, pkSeed, a); + } + return null; + }; + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG.java index fb6d817027..d9a4c3c79c 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class SIG { private final byte[] r; private final SIG_FORS[] sig_fors; private final SIG_XMSS[] sig_ht; public SIG(int n, int k, int a, int d, int hPrime, int wots_len, byte[] signature) { this.r = new byte[n]; System.arraycopy(signature, 0, r, 0, n); this.sig_fors = new SIG_FORS[k]; int offset = n; for (int i = 0; i != k; i++) { diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_FORS.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_FORS.java index 7c00647622..5d3c5dcff0 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_FORS.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_FORS.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class SIG_FORS { final byte[][] authPath; final byte[] sk; SIG_FORS(byte[] sk, byte[][] authPath) { this.authPath = authPath; this.sk = sk; } byte[] getSK() { return sk; } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_XMSS.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_XMSS.java index f77ffaf1cb..0d64d2a7d9 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_XMSS.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SIG_XMSS.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class SIG_XMSS { final byte[] sig; final byte[][] auth; public SIG_XMSS(byte[] sig, byte[][] auth) { this.sig = sig; this.auth = auth; } public byte[] getWOTSSig() { return sig; } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SK.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SK.java index 8040421b15..4ea009d94d 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SK.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SK.java @@ -1,24 +1,26 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") class SK { final byte[] seed; final byte[] prf; SK(byte[] seed, byte[] prf) { this.seed = seed; this.prf = prf; } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngine.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngine.java index bf7b40087f..69fcce3dea 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngine.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngine.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Xof; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.generators.MGF1BytesGenerator; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.MGFParameters; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Memoable; import org.bouncycastle.util.Pack; @SuppressWarnings("all") abstract class SLHDSAEngine { final int N; diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngineProvider.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngineProvider.java index 791705d9ad..b15b4c7ae9 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngineProvider.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAEngineProvider.java @@ -1,19 +1,21 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") interface SLHDSAEngineProvider { int getN(); SLHDSAEngine get(); } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyGenerationParameters.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyGenerationParameters.java index b3daec9773..12b23ac26a 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyGenerationParameters.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyGenerationParameters.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.security.SecureRandom; import org.bouncycastle.crypto.KeyGenerationParameters; @SuppressWarnings("all") public class SLHDSAKeyGenerationParameters extends KeyGenerationParameters { private final SLHDSAParameters parameters; public SLHDSAKeyGenerationParameters(SecureRandom random, SLHDSAParameters parameters) { super(random, -1); this.parameters = parameters; } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyPairGenerator.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyPairGenerator.java index 58a32e8ea9..32879f8331 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyPairGenerator.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyPairGenerator.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.security.SecureRandom; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; @SuppressWarnings("all") public class SLHDSAKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { private SecureRandom random; private SLHDSAParameters parameters; public void init(KeyGenerationParameters param) { random = param.getRandom(); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyParameters.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyParameters.java index 2999c4cac5..d13985c1d7 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyParameters.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAKeyParameters.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; @SuppressWarnings("all") public class SLHDSAKeyParameters extends AsymmetricKeyParameter { private final SLHDSAParameters parameters; protected SLHDSAKeyParameters(boolean isPrivate, SLHDSAParameters parameters) { super(isPrivate); this.parameters = parameters; } public SLHDSAParameters getParameters() { diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAParameters.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAParameters.java index 4a5e422c8c..1d14c94d73 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAParameters.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAParameters.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; @SuppressWarnings("all") public class SLHDSAParameters { public static final int TYPE_PURE = 0; public static final int TYPE_SHA2_256 = 1; public static final int TYPE_SHA2_512 = 2; public static final int TYPE_SHAKE128 = 3; public static final int TYPE_SHAKE256 = 4; // "Pure" SLH-DSA Parameters // SHA-2 public static final SLHDSAParameters sha2_128f = new SLHDSAParameters( "sha2-128f", new Sha2EngineProvider(16, 16, 22, 6, 33, 66), TYPE_PURE); public static final SLHDSAParameters sha2_128s = new SLHDSAParameters( "sha2-128s", new Sha2EngineProvider(16, 16, 7, 12, 14, 63), TYPE_PURE); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPrivateKeyParameters.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPrivateKeyParameters.java index da9c060bb4..64e42635c8 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPrivateKeyParameters.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPrivateKeyParameters.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.util.Arrays; @SuppressWarnings("all") public class SLHDSAPrivateKeyParameters extends SLHDSAKeyParameters { final SK sk; final PK pk; public SLHDSAPrivateKeyParameters(SLHDSAParameters parameters, byte[] skpkEncoded) { super(true, parameters); int n = parameters.getN(); if (skpkEncoded.length != 4 * n) { throw new IllegalArgumentException("private key encoding does not match parameters"); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPublicKeyParameters.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPublicKeyParameters.java index 544156c66c..5492012f73 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPublicKeyParameters.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSAPublicKeyParameters.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.util.Arrays; @SuppressWarnings("all") public class SLHDSAPublicKeyParameters extends SLHDSAKeyParameters { private final PK pk; public SLHDSAPublicKeyParameters(SLHDSAParameters parameters, byte[] pkValues) { super(false, parameters); int n = parameters.getN(); if (pkValues.length != 2 * n) { throw new IllegalArgumentException("public key encoding does not match parameters"); } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSASigner.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSASigner.java index 91fdef108b..720e4f2414 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSASigner.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SLHDSASigner.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.ParametersWithContext; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.util.Arrays; /** * SLH-DA signer. *

    * This version is based on the 3rd submission with deference to the updated reference * implementation on github as at November 9th 2021. This version includes the changes * for the countermeasure for the long-message second preimage attack - see * "https://github.com/sphincs/sphincsplus/commit/61cd2695c6f984b4f4d6ed675378ed9a486cbede" * for further details. @@ -174,40 +176,47 @@ public class SLHDSASigner adrs.setKeyPairAddress(idx_leaf); byte[] PK_FORS = new Fors(engine).pkFromSig(sig_fors, mHash, pubKey.getSeed(), adrs); // verify HT signature adrs.setTypeAndClear(ADRS.TREE); adrs.setLayerAddress(0); adrs.setTreeAddress(idx_tree); adrs.setKeyPairAddress(idx_leaf); HT ht = new HT(engine, null, pubKey.getSeed()); return ht.verify(PK_FORS, SIG_HT, pubKey.getSeed(), idx_tree, idx_leaf, pubKey.getRoot()); } protected byte[] internalGenerateSignature(byte[] message, byte[] optRand) { return internalGenerateSignature(privKey, null, message, optRand); } private static byte[] internalGenerateSignature(SLHDSAPrivateKeyParameters privKey, byte[] msgPrefix, byte[] msg, byte[] optRand) { // TODO Check init via privKey != null + // AERE (D-337, 2026-09-26): the same signature, bit for bit, with the message-independent work on + // the free cores (ParallelSigning). null = not taken, and the original code below runs unchanged. + byte[] aere = ParallelSigning.trySign(privKey, msgPrefix, msg, optRand); + if (aere != null) + { + return aere; + } SLHDSAEngine engine = privKey.getParameters().getEngine(); engine.init(privKey.pk.seed); Fors fors = new Fors(engine); byte[] R = engine.PRF_msg(privKey.sk.prf, optRand, msgPrefix, msg); IndexedDigest idxDigest = engine.H_msg(R, privKey.pk.seed, privKey.pk.root, msgPrefix, msg); byte[] mHash = idxDigest.digest; long idx_tree = idxDigest.idx_tree; int idx_leaf = idxDigest.idx_leaf; // FORS sign ADRS adrs = new ADRS(); adrs.setTypeAndClear(ADRS.FORS_TREE); adrs.setTreeAddress(idx_tree); adrs.setKeyPairAddress(idx_leaf); SIG_FORS[] sig_fors = fors.sign(mHash, privKey.sk.seed, privKey.pk.seed, adrs); // get FORS public key - spec shows M? adrs = new ADRS(); adrs.setTypeAndClear(ADRS.FORS_TREE); diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/WotsPlus.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/WotsPlus.java index fe1a94e580..47600d14d3 100755 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/WotsPlus.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/slhdsa/WotsPlus.java @@ -1,28 +1,30 @@ /* * Derived from Bouncy Castle 1.80, package org.bouncycastle.pqc.crypto.slhdsa (MIT licence, * Copyright (c) 2000-2025 The Legion of the Bouncy Castle Inc., see LICENTA-BOUNCYCASTLE.txt), * regenerated into Aere Network by consensus-pqc/slhdsa-fork/genereaza-motorul.py on 2026-09-04 (D-337). - * The ONLY change of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI - * intrinsics) instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature - * cost seconds on the QBFT thread at every anchor parent. Algorithm, key formats and signature bytes are - * FIPS 205 as before; SlhDsaFastEngineTest pins that against the original, in both directions. + * Changes of substance: the SHA-2 engine hashes through the JDK's MessageDigest (SHA-NI intrinsics) + * instead of Bouncy Castle's pure-Java digests, which made one SLH-DSA-SHA2-128s signature cost seconds on + * the QBFT thread at every anchor parent; and (2026-09-26) signing first tries ParallelSigning, which + * spreads the message-independent work over the cores and gives the same bytes (HT gains a constructor + * without the unused top-layer public key). Algorithm, key formats and signature bytes are FIPS 205 as + * before; SlhDsaFastEngineTest and SlhDsaParallelSigningTest pin that against the original. * DO NOT EDIT BY HAND: regenerate with the script above. */ package org.hyperledger.besu.consensus.common.bft.slhdsa; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @SuppressWarnings("all") class WotsPlus { private final SLHDSAEngine engine; private final int w; WotsPlus(SLHDSAEngine engine) { this.engine = engine; this.w = this.engine.WOTS_W; } byte[] pkGen(byte[] skSeed, byte[] pkSeed, ADRS paramAdrs) diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/tools/PqRegistryV3Tool.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/tools/PqRegistryV3Tool.java new file mode 100644 index 0000000000..ec81e7d03f --- /dev/null +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/tools/PqRegistryV3Tool.java @@ -0,0 +1,288 @@ +/* + * AERE AIP-22 stage 3: the tool that makes a registry v3 epoch FROM THE KEYS ALREADY ON THE VALIDATORS. + * + * Two commands, and the split is the security property: + * + * row runs ON THE HOST that holds a validator's keys. It reads that validator's Falcon-512 key + * file (the same "index, f, g, F, pk" properties the node loads through aere.falcon.key), + * its SLH-DSA-SHA2-128s key file (the same "index, sk" the hybrid loader reads) and its ECDSA + * validator key (a file holding the 32-byte hex), and prints ONE row: the id derived from the + * Falcon key, both public keys in their standard encodings, a proof of possession by each, and + * the ecdsa link signed by the validator key. It prints no private key, nor any part of one. + * assemble runs anywhere (the laptop): it takes the rows, builds the manifest, LOADS it through the + * strict loader (every proof and claim is verified again) and prints the manifest and its + * registry hash - the value the chain pins. + * + * Nothing leaves a host but signatures and public keys, which is what "no new key material and nothing + * out of the vault" means for this ceremony (AIP-22 section 3.2, the plan's stage 3). + * + * java -cp '/*' org.hyperledger.besu.consensus.common.bft.tools.PqRegistryV3Tool row \ + * --chain-id 28001 --bind H --count N --index i --falcon-key F --slh-key S --slh-pub HEX \ + * --ecdsa-key-file K [--transport] + * java -cp '/*' ...PqRegistryV3Tool assemble --chain-id 28001 --bind H --count N \ + * --required falcon-512,slh-dsa-sha2-128s row0.json row1.json ... + */ +package org.hyperledger.besu.consensus.common.bft.tools; + +import org.hyperledger.besu.consensus.common.bft.FalconSealScheme; +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.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECPPrivateKey; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.core.Util; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.bouncycastle.pqc.crypto.falcon.FalconParameters; +import org.bouncycastle.pqc.crypto.falcon.FalconPrivateKeyParameters; + +/** The registry v3 ceremony tool. */ +public final class PqRegistryV3Tool { + + private static final ObjectMapper M = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + + private PqRegistryV3Tool() {} + + /** + * Entry point. + * + * @param args the command and its options + * @throws IOException on unreadable input + */ + public static void main(final String[] args) throws IOException { + if (args.length == 0) { + usage(); + System.exit(2); + } + final Map o = new HashMap<>(); + final List files = new ArrayList<>(); + for (int i = 1; i < args.length; i++) { + if (args[i].startsWith("--")) { + if (args[i].equals("--transport")) { + o.put("transport", "1"); + } else if (i + 1 < args.length) { + o.put(args[i].substring(2), args[++i]); + } else { + usage(); + System.exit(2); + } + } else { + files.add(args[i]); + } + } + switch (args[0]) { + case "keygen" -> keygen(o); + case "row" -> row(o); + case "assemble" -> assemble(o, files); + default -> { + usage(); + System.exit(2); + } + } + } + + /** + * AIP-22 stage 3b: a NEW key pair for a validator, generated ON THE HOST that will hold it (a rotation). Writes the Falcon-512 + * key file (index, f, g, F, pk - the format aere.falcon.key and aere.falcon.keys.next read) and the SLH-DSA-SHA2-128s key file + * (index, alg, sk, pk - the format the hybrid loaders read), owner-only, refusing to overwrite anything; prints only the public + * keys. + */ + private static void keygen(final Map o) throws IOException { + final int index = Integer.parseInt(need(o, "index")); + final Path fOut = Path.of(need(o, "out-falcon")); + final Path sOut = Path.of(need(o, "out-slh")); + if (Files.exists(fOut) || Files.exists(sOut)) { + throw new IllegalStateException("refusing to overwrite an existing key file"); + } + final java.security.SecureRandom rng = + org.hyperledger.besu.crypto.SecureRandomProvider.createSecureRandom(); + final org.bouncycastle.pqc.crypto.falcon.FalconKeyPairGenerator g = + new org.bouncycastle.pqc.crypto.falcon.FalconKeyPairGenerator(); + g.init(new org.bouncycastle.pqc.crypto.falcon.FalconKeyGenerationParameters(rng, FalconParameters.falcon_512)); + final FalconPrivateKeyParameters fk = (FalconPrivateKeyParameters) g.generateKeyPair().getPrivate(); + final SealScheme slh = SealSchemes.SLH_DSA_128S; + final SealScheme.GeneratedPair sp = slh.generate(rng); + final byte[] slhSk = slh.serializePrivateKey(sp.privateKey()).orElseThrow(); + final String falconFile = + "index=" + index + "\nf=" + hx(fk.getSpolyf()).substring(2) + "\ng=" + hx(fk.getG()).substring(2) + + "\nF=" + hx(fk.getSpolyF()).substring(2) + "\npk=" + hx(fk.getPublicKey()).substring(2) + "\n"; + final String slhFile = + "index=" + index + "\nalg=slh-dsa-sha2-128s\nsk=" + hx(slhSk).substring(2) + "\npk=" + + hx(sp.publicRegistryForm()).substring(2) + "\n"; + final java.nio.file.attribute.FileAttribute ownerOnly = + java.nio.file.attribute.PosixFilePermissions.asFileAttribute( + java.nio.file.attribute.PosixFilePermissions.fromString("rw-------")); + Files.writeString(Files.createFile(fOut, ownerOnly), falconFile, StandardCharsets.UTF_8); + Files.writeString(Files.createFile(sOut, ownerOnly), slhFile, StandardCharsets.UTF_8); + final ObjectNode out = M.createObjectNode(); + out.put("index", index); + out.put("falconPk", hx(fk.getPublicKey())); + out.put("slhPub", hx(sp.publicRegistryForm())); + System.out.println(M.writeValueAsString(out)); + } + + private static void row(final Map o) throws IOException { + final long chain = Long.parseLong(need(o, "chain-id")); + final long bind = Long.parseLong(need(o, "bind")); + final int count = Integer.parseInt(need(o, "count")); + final int index = Integer.parseInt(need(o, "index")); + // Falcon: the node's own key file; the index inside must be the index asked for + final Properties fp = new Properties(); + fp.load(new StringReader(Files.readString(Path.of(need(o, "falcon-key")), StandardCharsets.UTF_8))); + if (Integer.parseInt(fp.getProperty("index").trim()) != index) { + throw new IllegalArgumentException("the Falcon key file is for index " + fp.getProperty("index")); + } + final byte[] h = hex(fp.getProperty("pk")); + final FalconPrivateKeyParameters fsk = + new FalconPrivateKeyParameters( + FalconParameters.falcon_512, hex(fp.getProperty("f")), hex(fp.getProperty("g")), + hex(fp.getProperty("F")), h); + final byte[] falconStd = new byte[PqRegistryV3.FALCON_512_PK_LENGTH]; + falconStd[0] = PqRegistryV3.FALCON_512_PK_HEADER; + System.arraycopy(h, 0, falconStd, 1, h.length); + // AIP-22 stage 3b: a ROTATED row keeps the id of its first registration (--id); a new row's id is its Falcon key's derivation + final byte[] id = o.containsKey("id") ? hex(o.get("id")) : PqRegistryV3.idOf(falconStd); + final byte[] falconPop = + falconSign(fsk, PqRegistryV3.popMessage(chain, bind, count, index, id, SealSchemes.FALCON_512.wireId(), falconStd)); + // SLH-DSA: the hybrid key file; the public key is given (it is the registry's), and the private key + // must sign a probe that verifies under it before anything is printed + final Properties sp = new Properties(); + sp.load(new StringReader(Files.readString(Path.of(need(o, "slh-key")), StandardCharsets.UTF_8))); + if (Integer.parseInt(sp.getProperty("index").trim()) != index) { + throw new IllegalArgumentException("the SLH-DSA key file is for index " + sp.getProperty("index")); + } + final SealScheme slh = SealSchemes.SLH_DSA_128S; + final SealScheme.PrivateHandle ssk = + slh.parsePrivateKey(hex(sp.getProperty("sk"))).orElseThrow( + () -> new IllegalArgumentException("the SLH-DSA key does not parse")); + final byte[] slhPub = hex(need(o, "slh-pub")); + final byte[] slhPop = + slh.sign(ssk, PqRegistryV3.popMessage(chain, bind, count, index, id, slh.wireId(), slhPub).toArrayUnsafe()) + .orElseThrow(); + if (!slh.verifyRaw(slhPub, PqRegistryV3.popMessage(chain, bind, count, index, id, slh.wireId(), slhPub).toArrayUnsafe(), slhPop)) { + throw new IllegalStateException("the SLH-DSA key file does not match the public key given; refusing"); + } + // the ECDSA validator key: signs the link from its address to the row + final SignatureAlgorithm ec = SignatureAlgorithmFactory.getInstance(); + final String ecHex = Files.readString(Path.of(need(o, "ecdsa-key-file")), StandardCharsets.UTF_8).trim(); + final SECPPrivateKey ecsk = ec.createPrivateKey(Bytes32.fromHexString(ecHex)); + final KeyPair kp = ec.createKeyPair(ecsk); + final byte[] addr = Util.publicKeyToAddress(kp.getPublicKey()).getBytes().toArrayUnsafe(); + final TreeMap keys = new TreeMap<>(); + keys.put(SealSchemes.FALCON_512.wireId() & 0xff, new PqRegistryV3.Key(SealSchemes.FALCON_512, falconStd, falconPop)); + keys.put(slh.wireId() & 0xff, new PqRegistryV3.Key(slh, slhPub, slhPop)); + final byte[] transport = o.containsKey("transport") ? addr : null; + final Bytes32 rh = + PqRegistryV3.rowHash(chain, bind, count, new PqRegistryV3.Row(index, id, keys, transport, Map.of(), null, null)); + final byte[] claim = ec.sign(PqRegistryV3.claimDigest(chain, rh), kp).encodedBytes().toArrayUnsafe(); + final ObjectNode row = M.createObjectNode(); + row.put("id", hx(id)); + row.putObject("keys").put("falcon-512", hx(falconStd)).put("slh-dsa-sha2-128s", hx(slhPub)); + row.putObject("pop").put("falcon-512", hx(falconPop)).put("slh-dsa-sha2-128s", hx(slhPop)); + if (transport != null) { + row.putObject("transport").put("secp256k1Address", hx(transport)); + } + row.putObject("ecdsa").put("addr", hx(addr)).put("claim", hx(claim)); + // AIP-22 stage 3b: a rotation - EACH old key of the row signs the new row's hash (AERE-PQ-ROTATE-3); the loader requires + // one signature per scheme the previous epoch bound, by exactly that previous key + if (o.containsKey("old-falcon-key") || o.containsKey("old-slh-key")) { + final Bytes32 rot = PqRegistryV3.rotateMessage(rh); + final Properties of = new Properties(); + of.load(new StringReader(Files.readString(Path.of(need(o, "old-falcon-key")), StandardCharsets.UTF_8))); + final FalconPrivateKeyParameters oldF = + new FalconPrivateKeyParameters( + FalconParameters.falcon_512, hex(of.getProperty("f")), hex(of.getProperty("g")), + hex(of.getProperty("F")), hex(of.getProperty("pk"))); + final Properties os = new Properties(); + os.load(new StringReader(Files.readString(Path.of(need(o, "old-slh-key")), StandardCharsets.UTF_8))); + final SealScheme.PrivateHandle oldS = + slh.parsePrivateKey(hex(os.getProperty("sk"))).orElseThrow( + () -> new IllegalArgumentException("the old SLH-DSA key does not parse")); + row.putObject("rotation") + .put("falcon-512", hx(falconSign(oldF, rot))) + .put("slh-dsa-sha2-128s", hx(slh.sign(oldS, rot.toArrayUnsafe()).orElseThrow())); + } + final ObjectNode out = M.createObjectNode(); + out.put("index", index); + out.set("row", row); + System.out.println(M.writeValueAsString(out)); + } + + private static void assemble(final Map o, final List files) throws IOException { + final long chain = Long.parseLong(need(o, "chain-id")); + final long bind = Long.parseLong(need(o, "bind")); + final int count = Integer.parseInt(need(o, "count")); + final List required = Arrays.asList(o.getOrDefault("required", "falcon-512").split(",")); + final ObjectNode m = M.createObjectNode(); + m.put("formatVersion", PqRegistryV3.FORMAT_VERSION); + m.put("chainId", chain); + m.put("bindHeight", bind); + m.put("count", count); + for (final String f : files) { + final JsonNode r = M.readTree(Files.readString(Path.of(f), StandardCharsets.UTF_8)); + m.set(Integer.toString(r.get("index").asInt()), r.get("row")); + } + // AIP-22 stage 3b: an epoch after the switch epoch is loaded AGAINST the one before it (--previous), so every rotation it + // carries is verified; the switch epoch itself (no --previous) must carry every ecdsa link + PqRegistryV3.Registry previous = null; + if (o.containsKey("previous")) { + final JsonNode pj = M.readTree(Files.readString(Path.of(o.get("previous")), StandardCharsets.UTF_8)); + previous = PqRegistryV3.load(pj, chain, required, null, true); + } + final PqRegistryV3.Registry reg = PqRegistryV3.load(m, chain, required, previous, previous == null); + m.put("registryHash", reg.hash().toHexString()); + System.out.println(M.writeValueAsString(m)); + System.err.println("registryHash " + reg.hash().toHexString() + " (" + count + " rows, bound at " + bind + ")"); + } + + private static byte[] falconSign(final FalconPrivateKeyParameters sk, final Bytes32 message) { + return ((FalconSealScheme) SealSchemes.FALCON_512) + .signWithParams(sk, message.toArrayUnsafe()) + .orElseThrow(() -> new IllegalStateException("the Falcon key did not sign")); + } + + private static String need(final Map o, final String k) { + final String v = o.get(k); + if (v == null) { + throw new IllegalArgumentException("missing --" + k); + } + return v; + } + + private static byte[] hex(final String s) { + return Bytes.fromHexStringLenient(s.trim()).toArrayUnsafe(); + } + + private static String hx(final byte[] b) { + final StringBuilder sb = new StringBuilder("0x"); + for (final byte x : b) { + sb.append(String.format(Locale.ROOT, "%02x", x & 0xff)); + } + return sb.toString(); + } + + private static void usage() { + System.err.println("usage: PqRegistryV3Tool row|assemble ... (see the class comment)"); + } +} diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTally.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTally.java index 8251f5c94b..fed8a2a5a7 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTally.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTally.java @@ -1,36 +1,43 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.common.validator.blockbased; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.validator.ValidatorVote; import org.hyperledger.besu.datatypes.Address; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.NavigableSet; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import com.google.common.collect.Maps; /** Tracks the current list of validators and votes to add or drop validators. */ class VoteTally { private final NavigableSet

    currentValidators; @@ -93,40 +100,58 @@ class VoteTally { return Optional.ofNullable(addVotesBySubject.get(subject)).orElse(Collections.emptySet()); } Set
    getOutstandingRemoveVotesFor(final Address subject) { return Optional.ofNullable(removeVotesBySubject.get(subject)).orElse(Collections.emptySet()); } private int validatorLimit() { return (currentValidators.size() / 2) + 1; } /** * Reset the outstanding vote tallies as required at each epoch. The current validator list is * unaffected. */ void discardOutstandingVotes() { addVotesBySubject.clear(); removeVotesBySubject.clear(); } + /** + * AERE AIP-22 stage 3: after block H_id - 1 the validators become the sorted post-quantum ids of the + * same members, and votes still open on classical addresses are discarded (they name addresses the + * id space no longer contains). Anywhere else, and when the identity switch is not armed, nothing + * changes. + * + * @param blockNumber the block whose effects this tally now includes + */ + void translateToIds(final long blockNumber) { + final Collection
    translated = + AerePqIdentity.translateSetAfter(currentValidators, blockNumber); + if (translated != currentValidators) { + currentValidators.clear(); + currentValidators.addAll(translated); + discardOutstandingVotes(); + } + } + /** * The validator addresses * * @return The collection of validators after the voting at the most recent block has been * finalised. */ Collection
    getValidators() { return currentValidators; } VoteTally copy() { final Map> addVotesBySubject = Maps.newHashMap(); final Map> removeVotesBySubject = Maps.newHashMap(); this.addVotesBySubject.forEach( (key, value) -> addVotesBySubject.put(key, new TreeSet<>(value))); this.removeVotesBySubject.forEach( (key, value) -> removeVotesBySubject.put(key, new TreeSet<>(value))); return new VoteTally( diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyUpdater.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyUpdater.java index bae5540aad..0da2c63f55 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyUpdater.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyUpdater.java @@ -1,38 +1,45 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.common.validator.blockbased; import org.hyperledger.besu.consensus.common.BlockInterface; import org.hyperledger.besu.consensus.common.EpochManager; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.validator.ValidatorVote; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import java.util.Collection; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides the logic to extract vote tally state from the blockchain and update it as blocks are * added. */ class VoteTallyUpdater { private static final Logger LOG = LoggerFactory.getLogger(VoteTallyUpdater.class); private final EpochManager epochManager; @@ -56,26 +63,31 @@ class VoteTallyUpdater { final BlockHeader epochBlock = blockchain.getBlockHeader(epochBlockNumber).get(); final Collection
    initialValidators = blockInterface.validatorsInBlock(epochBlock); final VoteTally voteTally = new VoteTally(initialValidators); for (long blockNumber = epochBlockNumber + 1; blockNumber <= chainHeadBlockNumber; blockNumber++) { updateForBlock(blockchain.getBlockHeader(blockNumber).get(), voteTally); } return voteTally; } /** * Update the vote tally to reflect changes caused by appending a new block to the chain. * * @param header the header of the block being added * @param voteTally the vote tally to update */ void updateForBlock(final BlockHeader header, final VoteTally voteTally) { if (epochManager.isEpochBlock(header.getNumber())) { voteTally.discardOutstandingVotes(); - return; + } else { + final Optional vote = blockInterface.extractVoteFromHeader(header); + vote.ifPresent(voteTally::addVote); + } + // AERE AIP-22 stage 3: after block H_id - 1 the set becomes the post-quantum ids of its members; + // asked first, so that disarmed (or at any other block) the tally sees no extra call at all + if (AerePqIdentity.translatesAfter(header.getNumber())) { + voteTally.translateToIds(header.getNumber()); } - final Optional vote = blockInterface.extractVoteFromHeader(header); - vote.ifPresent(voteTally::addVote); } } diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereKeysFromV3Test.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereKeysFromV3Test.java new file mode 100644 index 0000000000..ba91cd1b81 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereKeysFromV3Test.java @@ -0,0 +1,192 @@ +/* + * AERE AIP-22 stage 3b: probes of the keys that seal coming from the v3 epoch in force (AerePqIdentity + * keysFromV3 / v3KeyAt / v3IdAt / verifyKeyContinuity) and of the ecdsa link inherited by id, on the two + * shared registry v3 conformance vectors: ok-epoch-a (bound at 1000, three rows, each with its ecdsa link) + * and ok-epoch-b-rotation (bound at 2000: index 1 rotates both keys under its id, the id of A's index 2 + * moves to index 0, A's index 0 leaves, a new id joins at index 2; no ecdsa links). + * + * What must hold, each with its negative: H_k falls on an epoch boundary at or above H_id or the node + * refuses to arm; below H_k nothing comes from v3; from H_k the key of an index is the key of THAT index in + * the epoch in force (Falcon without its 0x09 header, SLH-DSA as its 32 bytes), so the rotated key replaces + * the old one exactly at the rotation epoch and an index the epoch does not have gives NO key; the + * continuity guard accepts the same keys and refuses one changed byte or a missing key; and an author keeps + * its id across an epoch that omits the ecdsa links, but a removed id's address authors as nothing. + */ +package org.hyperledger.besu.consensus.common.bft; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.datatypes.Address; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AereKeysFromV3Test { + + private static final List SCHEMES = List.of("falcon-512", "slh-dsa-sha2-128s"); + private PqRegistryV3.Registry a; + private PqRegistryV3.Registry b; + private TreeMap epochs; + + private JsonNode vector(final String name) throws IOException { + try (InputStream in = getClass().getResourceAsStream("/aere/registry-v3/" + name + ".json")) { + assertThat(in).isNotNull(); + return new ObjectMapper().readTree(in); + } + } + + @BeforeEach + public void load() throws IOException { + final JsonNode va = vector("ok-epoch-a"); + final JsonNode vb = vector("ok-epoch-b-rotation"); + a = PqRegistryV3.load(va.get("manifest"), va.get("chainId").asLong(), SCHEMES, null, true); + b = PqRegistryV3.load(vb.get("manifest"), vb.get("chainId").asLong(), SCHEMES, a, false); + assertThat(a.bindHeight()).isEqualTo(1000L); + assertThat(b.bindHeight()).isEqualTo(2000L); + epochs = new TreeMap<>(); + epochs.put(a.bindHeight(), a); + epochs.put(b.bindHeight(), b); + AerePqIdentity.uninstall(); + } + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + private static byte[] key(final PqRegistryV3.Registry r, final int index, final String scheme) { + return r.rows().get(index).key(scheme).orElseThrow().publicKey(); + } + + private static Address id(final PqRegistryV3.Registry r, final int index) { + return Address.wrap(Bytes.wrap(r.rows().get(index).id())); + } + + private static Address ecdsa(final PqRegistryV3.Registry r, final int index) { + return Address.wrap(Bytes.wrap(r.rows().get(index).ecdsaAddress())); + } + + @Test + public void keysFromMustBeAnEpochBoundaryAtOrAboveTheIdentityHeight() { + assertThatThrownBy(() -> AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 1999))) + .isInstanceOf(AerePqIdentity.IdentityException.class) + .hasMessageContaining("must be the bindHeight of a v3 epoch"); + assertThatThrownBy(() -> AerePqIdentity.install(new AerePqIdentity.Config(2000, epochs, 1000))) + .isInstanceOf(AerePqIdentity.IdentityException.class); + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 2000)); + assertThat(AerePqIdentity.installed().orElseThrow().keysFrom()).isEqualTo(2000L); + } + + @Test + public void belowHkNothingComesFromV3AndStage3aNeverDoes() { + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 2000)); + assertThat(AerePqIdentity.keysFromV3(1999)).isFalse(); + assertThat(AerePqIdentity.keysFromV3(2000)).isTrue(); + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs)); + assertThat(AerePqIdentity.keysFromV3(Long.MAX_VALUE - 1)).isFalse(); + AerePqIdentity.uninstall(); + assertThat(AerePqIdentity.keysFromV3(5000)).isFalse(); + } + + @Test + public void theKeyIsThatIndexOfTheEpochInForceAndTheRotationTakesOverAtItsEpoch() { + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 1000)); + final byte[] fa1 = AerePqIdentity.v3KeyAt(1999, 1, "falcon-512").orElseThrow(); + final byte[] fb1 = AerePqIdentity.v3KeyAt(2000, 1, "falcon-512").orElseThrow(); + // the registry form: 896 bytes, the 0x09 header dropped + assertThat(fa1).hasSize(896); + assertThat(fa1).isEqualTo(Arrays.copyOfRange(key(a, 1, "falcon-512"), 1, 897)); + assertThat(fb1).isEqualTo(Arrays.copyOfRange(key(b, 1, "falcon-512"), 1, 897)); + assertThat(fb1).isNotEqualTo(fa1); + final byte[] sa1 = AerePqIdentity.v3KeyAt(1999, 1, "slh-dsa-sha2-128s").orElseThrow(); + assertThat(sa1).hasSize(32).isEqualTo(key(a, 1, "slh-dsa-sha2-128s")); + assertThat(AerePqIdentity.v3KeyAt(2000, 1, "slh-dsa-sha2-128s").orElseThrow()) + .isEqualTo(key(b, 1, "slh-dsa-sha2-128s")) + .isNotEqualTo(sa1); + // indices belong to the epoch: at 2000 index 0 is the validator that was index 2 in epoch A + assertThat(AerePqIdentity.v3KeyAt(2000, 0, "falcon-512").orElseThrow()) + .isEqualTo(Arrays.copyOfRange(key(a, 2, "falcon-512"), 1, 897)); + assertThat(AerePqIdentity.v3IdAt(2000, 0)).contains(id(a, 2)); + assertThat(AerePqIdentity.v3IdAt(2000, 2)).contains(id(b, 2)); + // an index the epoch does not have: NO key (fail closed), never a fall-back + assertThat(AerePqIdentity.v3KeyAt(2000, 3, "falcon-512")).isEmpty(); + assertThat(AerePqIdentity.v3KeyAt(2000, -1, "falcon-512")).isEmpty(); + assertThat(AerePqIdentity.v3IdAt(2000, 3)).isEmpty(); + } + + @Test + public void theContinuityGuardAcceptsTheSameKeysAndRefusesAnyOther() { + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 1000)); + // the existing registries give exactly the keys of epoch A: accepted + AerePqIdentity.verifyKeyContinuity( + (i, s) -> AerePqIdentity.v3KeyAt(1000, i, s).map(byte[]::clone)); + // one byte of one SLH-DSA key differs: refused, naming the row and the scheme + assertThatThrownBy( + () -> + AerePqIdentity.verifyKeyContinuity( + (i, s) -> { + final byte[] k = AerePqIdentity.v3KeyAt(1000, i, s).orElseThrow().clone(); + if (i == 2 && s.equals("slh-dsa-sha2-128s")) { + k[5] ^= 1; + } + return Optional.of(k); + })) + .isInstanceOf(AerePqIdentity.IdentityException.class) + .hasMessageContaining("row 2 slh-dsa-sha2-128s differs"); + // a key the existing registries cannot produce: refused + assertThatThrownBy( + () -> + AerePqIdentity.verifyKeyContinuity( + (i, s) -> i == 0 ? Optional.empty() : AerePqIdentity.v3KeyAt(1000, i, s))) + .isInstanceOf(AerePqIdentity.IdentityException.class) + .hasMessageContaining("row 0"); + // no H_k configured: the guard is inert even with a function that has no keys at all + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs)); + AerePqIdentity.verifyKeyContinuity((i, s) -> Optional.empty()); + } + + @Test + public void theFalconSealSupportDoorsTakeTheirKeyAndIdFromV3FromHk() { + // the WIRING, not only the helper: FalconSealSupport.keyAt (behind every Falcon verification) and the index -> identity doors + // must answer from the v3 epoch in force from H_k. This node has no registry schedule at all, so any answer from H_k on can + // only come from v3; below H_k the doors fall back to the (absent) registries and answer nothing. + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 2000)); + final FalconSealSupport fs = FalconSealSupport.instance(); + assertThat(fs.publicKeyBytesAtHistoric(2000, 1)) + .contains(Arrays.copyOfRange(key(b, 1, "falcon-512"), 1, 897)); + assertThat(fs.publicKeyBytesAtHistoric(2000, 0)) + .contains(Arrays.copyOfRange(key(a, 2, "falcon-512"), 1, 897)); + assertThat(fs.publicKeyBytesAtHistoric(2000, 3)).isEmpty(); + assertThat(fs.addressForIndexAtHistoric(2000, 2)).isEqualTo(id(b, 2)); + assertThat(fs.addressForIndexAtOwnHead(2000, 0)).isEqualTo(id(a, 2)); + assertThat(fs.addressForIndexAtHistoric(2000, 3)).isNull(); + // below H_k: not from v3 (a v3 answer here would be the rotation taking effect early) + assertThat(fs.publicKeyBytesAtHistoric(1999, 1)) + .isNotEqualTo(Optional.of(Arrays.copyOfRange(key(a, 1, "falcon-512"), 1, 897))); + } + + @Test + public void anAuthorKeepsItsIdAcrossAnEpochWithoutLinksAndARemovedIdAuthorsNothing() { + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 1000)); + // epoch B carries no ecdsa links; the id of A's index 1 is still registered, so its address keeps it + assertThat(b.rows().get(1).ecdsaAddress()).isNull(); + assertThat(AerePqIdentity.author(ecdsa(a, 1), 1999)).isEqualTo(id(a, 1)); + assertThat(AerePqIdentity.author(ecdsa(a, 1), 2000)).isEqualTo(id(a, 1)); + assertThat(AerePqIdentity.author(ecdsa(a, 2), 2500)).isEqualTo(id(a, 2)); + // A's index 0 left in B: its address is itself again, which is not in the set + assertThat(AerePqIdentity.author(ecdsa(a, 0), 1999)).isEqualTo(id(a, 0)); + assertThat(AerePqIdentity.author(ecdsa(a, 0), 2000)).isEqualTo(ecdsa(a, 0)); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereLocalKeysV3Test.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereLocalKeysV3Test.java new file mode 100644 index 0000000000..e67a5bf730 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AereLocalKeysV3Test.java @@ -0,0 +1,164 @@ +/* + * AERE AIP-22 stage 3b-ii: probes of the LOCAL keys by height - a node signs, from H_k, with the key of its own row in the v3 + * epoch in force and at that row's index - on two epochs built here with PROBA keys this test holds (the shared vectors carry no + * private keys): epoch A bound at 1000 (v0, v1, v2 at indices 0, 1, 2, the switch epoch) and epoch B bound at 2000 (v2 MOVED to + * index 0, v1 ROTATED both keys under its id at index 1 with the rotation signed by its old keys, v0 left, a new v3 at index 2). + * + * What must hold, each with its negative: a node finds its row by its key and the index moves with the epoch (a key that no row + * of the epoch carries finds nothing); the hybrid producer of a rotating node signs with its OLD key up to the rotation epoch and + * with its NEXT key from it, at the right index, each seal verifying under exactly one of the two keys; a moved node signs at its + * new index; and a node that lacks its rotated key produces nothing from the rotation epoch (the fact the startup guard refuses on). + */ +package org.hyperledger.besu.consensus.common.bft; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.function.LongToIntFunction; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AereLocalKeysV3Test { + + private static final List SCHEMES = List.of("falcon-512", "slh-dsa-sha2-128s"); + private static final String SLH = "slh-dsa-sha2-128s"; + private PqRegistryV3Test.Proba v0; + private PqRegistryV3Test.Proba v1; + private PqRegistryV3Test.Proba v2; + private PqRegistryV3Test.Proba v1b; + private PqRegistryV3Test.Proba v3; + + @BeforeEach + public void build() { + v0 = PqRegistryV3Test.proba(); + v1 = PqRegistryV3Test.proba(); + v2 = PqRegistryV3Test.proba(); + v1b = PqRegistryV3Test.proba(); + v3 = PqRegistryV3Test.proba(); + final var d0 = new PqRegistryV3Test.Draft(PqRegistryV3.idOf(v0.falconStd()), PqRegistryV3Test.both(v0), v0); + final var d1 = new PqRegistryV3Test.Draft(PqRegistryV3.idOf(v1.falconStd()), PqRegistryV3Test.both(v1), v1); + final var d2 = new PqRegistryV3Test.Draft(PqRegistryV3.idOf(v2.falconStd()), PqRegistryV3Test.both(v2), v2); + final ObjectNode ma = + PqRegistryV3Test.manifest( + 1000, + List.of( + PqRegistryV3Test.rowJson(1000, 3, 0, d0, true, true), + PqRegistryV3Test.rowJson(1000, 3, 1, d1, true, false), + PqRegistryV3Test.rowJson(1000, 3, 2, d2, true, false))); + final PqRegistryV3.Registry a = PqRegistryV3.load(ma, PqRegistryV3Test.CHAIN, SCHEMES, null, true); + final var d1b = new PqRegistryV3Test.Draft(d1.id(), PqRegistryV3Test.both(v1b), v1); + final var d3 = new PqRegistryV3Test.Draft(PqRegistryV3.idOf(v3.falconStd()), PqRegistryV3Test.both(v3), v3); + final ObjectNode r1b = PqRegistryV3Test.rowJson(2000, 3, 1, d1b, false, false); + PqRegistryV3Test.rotate(r1b, 2000, 3, 1, List.of(v1.falcon(), v1.slh())); + final ObjectNode mb = + PqRegistryV3Test.manifest( + 2000, + List.of( + PqRegistryV3Test.rowJson(2000, 3, 0, d2, false, false), + r1b, + PqRegistryV3Test.rowJson(2000, 3, 2, d3, false, false))); + final PqRegistryV3.Registry b = PqRegistryV3.load(mb, PqRegistryV3Test.CHAIN, SCHEMES, a, false); + final TreeMap epochs = new TreeMap<>(); + epochs.put(1000L, a); + epochs.put(2000L, b); + AerePqIdentity.uninstall(); + AerePqIdentity.install(new AerePqIdentity.Config(1000, epochs, 1000)); + } + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + private static byte[] falcon(final PqRegistryV3Test.Proba p) { + return p.falcon().publicRegistryForm(); + } + + /** The local index a node holding these Falcon keys signs as at a height (what FalconSealSupport.localAt resolves). */ + private static LongToIntFunction indexOf(final PqRegistryV3Test.Proba... keys) { + return h -> { + for (final PqRegistryV3Test.Proba k : keys) { + final Optional i = AerePqIdentity.v3IndexOfKey(h, "falcon-512", falcon(k)); + if (i.isPresent()) { + return i.get(); + } + } + return -1; + }; + } + + private static HybridSealProducer producer( + final PqRegistryV3Test.Proba base, final List next, final LongToIntFunction idx) { + return new HybridSealProducer( + 0L, + PqSchemeSchedule.parse("0:falcon-512+" + SLH), + -1, + Map.of(SLH, base.slh().privateKey()), + 0L, + next.isEmpty() ? Map.of() : Map.of(SLH, next.stream().map(p -> p.slh().privateKey()).toList()), + idx); + } + + private static boolean verifies(final SchemeSeal s, final PqRegistryV3Test.Proba key, final Bytes msg) { + return SealSchemes.SLH_DSA_128S.verifyRaw( + key.slh().publicRegistryForm(), msg.toArrayUnsafe(), s.getSignature().toArrayUnsafe()); + } + + @Test + public void aNodeFindsItsRowByItsKeyAndTheIndexMovesWithTheEpoch() { + assertThat(AerePqIdentity.v3IndexOfKey(1999, "falcon-512", falcon(v2))).contains(2); + assertThat(AerePqIdentity.v3IndexOfKey(2000, "falcon-512", falcon(v2))).contains(0); + assertThat(AerePqIdentity.v3IndexOfKey(1999, "falcon-512", falcon(v1))).contains(1); + assertThat(AerePqIdentity.v3IndexOfKey(2000, "falcon-512", falcon(v1))).isEmpty(); + assertThat(AerePqIdentity.v3IndexOfKey(2000, "falcon-512", falcon(v1b))).contains(1); + assertThat(AerePqIdentity.v3IndexOfKey(1999, "falcon-512", falcon(v1b))).isEmpty(); + assertThat(AerePqIdentity.v3IndexOfKey(2000, "falcon-512", falcon(v0))).isEmpty(); + } + + @Test + public void aRotatingNodeSignsWithItsOldKeyUpToTheRotationEpochAndItsNextKeyFromIt() { + final HybridSealProducer p = producer(v1, List.of(v1b), indexOf(v1, v1b)); + final Bytes msg = Bytes.wrap("AERE-TEST-COMMIT".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + final List before = p.sealsFor(1999, msg); + assertThat(before).hasSize(1); + assertThat(before.get(0).getValidatorIndex()).isEqualTo(1); + assertThat(verifies(before.get(0), v1, msg)).isTrue(); + assertThat(verifies(before.get(0), v1b, msg)).isFalse(); + final List after = p.sealsFor(2000, msg); + assertThat(after).hasSize(1); + assertThat(after.get(0).getValidatorIndex()).isEqualTo(1); + assertThat(verifies(after.get(0), v1b, msg)).isTrue(); + assertThat(verifies(after.get(0), v1, msg)).isFalse(); + } + + @Test + public void aMovedNodeSignsAtItsNewIndex() { + final HybridSealProducer p = producer(v2, List.of(), indexOf(v2)); + final Bytes msg = Bytes.wrap("AERE-TEST-COMMIT".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + assertThat(p.sealsFor(1999, msg).get(0).getValidatorIndex()).isEqualTo(2); + final List after = p.sealsFor(2000, msg); + assertThat(after.get(0).getValidatorIndex()).isEqualTo(0); + assertThat(verifies(after.get(0), v2, msg)).isTrue(); + } + + @Test + public void aNodeWithoutItsRotatedKeyProducesNothingFromTheRotationEpoch() { + final HybridSealProducer p = producer(v1, List.of(), indexOf(v1, v1b)); + final Bytes msg = Bytes.wrap("AERE-TEST-COMMIT".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + assertThat(p.sealsFor(1999, msg)).hasSize(1); + // its Falcon index is resolvable only through v1b, which this node does not hold: nothing, and the guard's basis is empty + final HybridSealProducer onlyOld = producer(v1, List.of(), indexOf(v1)); + assertThat(onlyOld.sealsFor(2000, msg)).isEmpty(); + assertThat(onlyOld.v3KeyAt(SealSchemes.SLH_DSA_128S, 2000)).isEmpty(); + // holding the rotated Falcon key but not the rotated SLH-DSA key: the row's SLH key has no local match either + assertThat(p.v3KeyAt(SealSchemes.SLH_DSA_128S, 2000)).isEmpty(); + assertThat(p.sealsFor(2000, msg)).isEmpty(); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityAuthorTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityAuthorTest.java new file mode 100644 index 0000000000..4b875f937b --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityAuthorTest.java @@ -0,0 +1,243 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.consensus.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.crypto.Hash; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.Util; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +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 4 (iii): from H_fin the author of a message is the id of its post-quantum seal's index, + * ONCE THE SEAL VERIFIES, and the ECDSA signature of the envelope is not read. Every message here is + * signed by a FOREIGN key (not any validator's), so a validator's id can only come from the seal. The + * verifier is injected with the seal layers' contract (height, index, message, signature): it accepts + * exactly the signature {@code keccak(index || message)}, so a seal of another index, over another + * message, or of any other bytes does not verify. A COMMIT's author is its index's id only when that is + * the commit seal signer's id. Below H_fin, and with H_fin unset, the author is the (translated) ECDSA + * signer, as in stage 3. The runner's negative controls plant "H_fin never active" and "every seal + * verifies" at run time and these probes turn red. + */ +public class AerePqFinalityAuthorTest { + + private static final SignatureAlgorithm ALG = SignatureAlgorithmFactory.getInstance(); + private static final long CHAIN = 28001L; + private static final AerePqFinality.SealVerifier V = + (h, i, m, s) -> s.equals(sigFor(i, m)); + + private long hId; + private final List
    id = new ArrayList<>(); + private final List
    ecdsa = new ArrayList<>(); + private final KeyPair foreign = ALG.generateKeyPair(); + + private static Bytes sigFor(final int index, final Bytes message) { + // the index as its 32 bits (a negative index has a signature too, it just names no row) + return Hash.keccak256(Bytes.concatenate(Bytes.ofUnsignedLong(Integer.toUnsignedLong(index)), message)); + } + + private static Bytes32 messageAt(final long height) { + return Hash.keccak256(Bytes.concatenate(Bytes.wrap("t4-msg".getBytes(java.nio.charset.StandardCharsets.US_ASCII)), Bytes.ofUnsignedLong(height))); + } + + /** A PREPARE-like payload: its seal signs {@link #messageAt}(height); a COMMIT-like one names a commit seal signer. */ + private static final class Sealed implements Payload, PqSealed { + private final long height; + private final Optional seal; + private final Optional
    commitSigner; + private final boolean messageThrows; + + Sealed(final long height, final Optional seal, final Optional
    commitSigner, final boolean messageThrows) { + this.height = height; + this.seal = seal; + this.commitSigner = commitSigner; + this.messageThrows = messageThrows; + } + + @Override + public void writeTo(final RLPOutput out) { + out.startList(); + out.writeLongScalar(height); + out.endList(); + } + + @Override + public int getMessageType() { + return 0x12; + } + + @Override + public org.hyperledger.besu.datatypes.Hash hashForSignature() { + return org.hyperledger.besu.datatypes.Hash.hash(Bytes.ofUnsignedLong(height)); + } + + @Override + public ConsensusRoundIdentifier getRoundIdentifier() { + return new ConsensusRoundIdentifier(height, 0); + } + + @Override + public Optional getFalconSeal() { + return seal; + } + + @Override + public Optional pqSealMessage(final long chainId) { + if (messageThrows) { + throw new IllegalArgumentException("cannot build"); + } + return commitSigner.isPresent() || chainId != CHAIN ? Optional.empty() : Optional.of(messageAt(height)); + } + + @Override + public Optional
    commitSealSigner() { + return commitSigner; + } + } + + private Address signed(final Sealed p) { + return SignedData.create(p, ALG.sign(Bytes32.wrap(p.hashForSignature().getBytes()), foreign)).getAuthor(); + } + + private static Sealed prepare(final long height, final Optional seal) { + return new Sealed(height, seal, Optional.empty(), false); + } + + /** A seal of index {@code index} that verifies over the message of {@code signedHeight}. */ + private static Optional good(final int index, final long signedHeight) { + return Optional.of(new FalconSeal(index, sigFor(index, messageAt(signedHeight)))); + } + + @BeforeEach + public void arm() throws IOException { + final JsonNode v; + try (InputStream in = getClass().getResourceAsStream("/aere/registry-v3/ok-epoch-a.json")) { + assertThat(in).isNotNull(); + v = new ObjectMapper().readTree(in); + } + final PqRegistryV3.Registry epoch = + PqRegistryV3.load( + v.get("manifest"), v.get("chainId").asLong(), + List.of("falcon-512", "slh-dsa-sha2-128s"), null, true); + for (final PqRegistryV3.Row r : epoch.rows()) { + id.add(Address.wrap(Bytes.wrap(r.id()))); + ecdsa.add(Address.wrap(Bytes.wrap(r.ecdsaAddress()))); + } + hId = epoch.bindHeight(); + final TreeMap epochs = new TreeMap<>(); + epochs.put(hId, epoch); + AerePqFinality.uninstall(); + AerePqIdentity.uninstall(); + AerePqIdentity.install(new AerePqIdentity.Config(hId, epochs, hId)); + } + + @AfterEach + public void disarm() { + AerePqFinality.uninstall(); + AerePqIdentity.uninstall(); + } + + @Test + public void fromHfinTheAuthorIsTheIdOfAVerifiedSealWhateverKeySignedTheEnvelope() { + final long hFin = hId + 100; + AerePqFinality.install(hFin, CHAIN, V); + final Address foreignAddr = Util.publicKeyToAddress(foreign.getPublicKey()); + assertThat(id).doesNotContain(foreignAddr); + for (int i = 0; i < id.size(); i++) { + assertThat(signed(prepare(hFin, good(i, hFin)))).as("index %s at H_fin", i).isEqualTo(id.get(i)); + assertThat(signed(prepare(hFin + 500, good(i, hFin + 500)))).isEqualTo(id.get(i)); + } + // one block below H_fin the envelope's signer is still the author: a foreign key authors nothing of the set + assertThat(signed(prepare(hFin - 1, good(0, hFin - 1)))).isEqualTo(foreignAddr); + } + + @Test + public void fromHfinASealThatDoesNotVerifyAuthorsNothing() { + final long hFin = hId + 100; + AerePqFinality.install(hFin, CHAIN, V); + // the fixture can express the defect: the same index with its own seal IS an author + assertThat(signed(prepare(hFin, good(0, hFin)))).isEqualTo(id.get(0)); + // index 0 carrying index 1's seal + assertThat(signed(prepare(hFin, Optional.of(new FalconSeal(0, sigFor(1, messageAt(hFin))))))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + // index 0's seal over another height's message + assertThat(signed(prepare(hFin, good(0, hFin + 1)))).isEqualTo(AerePqFinality.NO_AUTHOR); + // any other bytes + assertThat(signed(prepare(hFin, Optional.of(new FalconSeal(0, Bytes.of(1, 2, 3)))))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + // no seal, an index outside the epoch, a negative index, a message that cannot be built + assertThat(signed(prepare(hFin, Optional.empty()))).isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat(signed(prepare(hFin, good(id.size(), hFin)))).isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat(signed(prepare(hFin, good(-1, hFin)))).isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat(signed(new Sealed(hFin, good(0, hFin), Optional.empty(), true))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + } + + @Test + public void fromHfinACommitsAuthorIsItsIndexOnlyWhenThatIsTheCommitSealSigner() { + final long hFin = hId + 100; + AerePqFinality.install(hFin, CHAIN, V); + final Optional anySeal = Optional.of(new FalconSeal(0, Bytes.of(9))); + assertThat(signed(new Sealed(hFin, anySeal, Optional.of(ecdsa.get(0)), false))).isEqualTo(id.get(0)); + assertThat(signed(new Sealed(hFin, anySeal, Optional.of(ecdsa.get(1)), false))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat( + signed( + new Sealed( + hFin, anySeal, Optional.of(Util.publicKeyToAddress(foreign.getPublicKey())), false))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat(signed(new Sealed(hFin, Optional.empty(), Optional.of(ecdsa.get(0)), false))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + } + + @Test + public void unsetHfinLeavesTheEcdsaSignerAsTheAuthorAndHfinNeedsKeysFromV3AndAChain() { + final Address foreignAddr = Util.publicKeyToAddress(foreign.getPublicKey()); + assertThat(signed(prepare(hId + 100, good(0, hId + 100)))).isEqualTo(foreignAddr); + // H_fin below H_k, without a chain id, or without keys read from the v3 epochs refuses to arm + assertThatThrownBy(() -> AerePqFinality.install(hId - 1, CHAIN, V)) + .isInstanceOf(IllegalStateException.class) + .hasMessageStartingWith("AERE-PQC-FIN-01"); + assertThatThrownBy(() -> AerePqFinality.install(hId + 100, 0, V)) + .isInstanceOf(IllegalStateException.class) + .hasMessageStartingWith("AERE-PQC-FIN-01"); + AerePqIdentity.uninstall(); + assertThatThrownBy(() -> AerePqFinality.install(hId + 100, CHAIN, V)) + .isInstanceOf(IllegalStateException.class) + .hasMessageStartingWith("AERE-PQC-FIN-01"); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTagsTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTagsTest.java new file mode 100755 index 0000000000..f171b392e0 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqFinalityTagsTest.java @@ -0,0 +1,235 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.plugin.data.AddedBlockContext.EventType; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * AERE D-412 Rule B1: finalized/safe follow the last certified anchor's parent, monotonically, and a + * forged between-anchor block can never be reported final. Anchors every 32 from 96 (K=3); the + * certificate predicate is a set of heights that "carry" one, so a planted defect in the rule (an + * anchor without certificate moving the tag, a lower height moving it back, a non-anchor moving it) + * turns exactly one of these red. + */ +class AerePqFinalityTagsTest { + + private static final long ANCHOR_BLOCK = 96L; + private static final int INTERVAL = 32; + + private PqAnchorConfig config; + private MutableBlockchain blockchain; + private final Map byNumber = new HashMap<>(); + private final Map byHash = new HashMap<>(); + private final Set certified = new HashSet<>(); + + @BeforeEach + void setUp() { + config = + new PqAnchorConfig(2800L, ANCHOR_BLOCK, Map.of(ANCHOR_BLOCK, 3), OptionalInt.empty(), false) + .withAnchorInterval(OptionalInt.of(INTERVAL)); + blockchain = mock(MutableBlockchain.class); + // a linear chain 0..200 with deterministic hashes; header(n).parentHash == hash(header(n-1)) + Hash prev = Hash.ZERO; + for (long n = 0; n <= 200; n++) { + final BlockHeader h = new BlockHeaderTestFixture().number(n).parentHash(prev).buildHeader(); + byNumber.put(n, h); + byHash.put(h.getHash(), h); + prev = h.getHash(); + } + when(blockchain.getBlockHeader(anyLong())).thenAnswer(i -> Optional.ofNullable(byNumber.get(i.getArgument(0, Long.class)))); + when(blockchain.getBlockHeader(any(Hash.class))).thenAnswer(i -> Optional.ofNullable(byHash.get(i.getArgument(0, Hash.class)))); + } + + private AerePqFinalityTags tagsWithHead(final long head) { + when(blockchain.getChainHeadHeader()).thenReturn(byNumber.get(head)); + return AerePqFinalityTags.install(config, blockchain, h -> certified.contains(h.getNumber())); + } + + private BlockAddedEvent headAdvanced(final long n) { + final BlockAddedEvent e = mock(BlockAddedEvent.class); + when(e.isNewCanonicalHead()).thenReturn(true); + when(e.getHeader()).thenReturn(byNumber.get(n)); + when(e.getEventType()).thenReturn(EventType.HEAD_ADVANCED); + return e; + } + + private BlockAddedEvent reorgTo(final long n, final long commonAncestor) { + final BlockAddedEvent e = mock(BlockAddedEvent.class); + when(e.isNewCanonicalHead()).thenReturn(true); + when(e.getHeader()).thenReturn(byNumber.get(n)); + when(e.getEventType()).thenReturn(EventType.CHAIN_REORG); + when(e.getCommonAncestorHash()).thenReturn(byNumber.get(commonAncestor).getHash()); + return e; + } + + @Test + void theConfigurationUnderTestHasAnchorsEveryThirtyTwoFromNinetySix() { + assertThat(config.anchorAppliesAt(96)).isTrue(); + assertThat(config.anchorAppliesAt(128)).isTrue(); + assertThat(config.anchorAppliesAt(129)).isFalse(); + assertThat(config.anchorAppliesAt(95)).isFalse(); + } + + @Test + void startupScanFinalizesTheParentOfTheLastCertifiedAnchorBelowTheHead() { + certified.add(96L); + certified.add(128L); + final AerePqFinalityTags tags = tagsWithHead(140); + verify(blockchain, times(1)).setFinalized(byNumber.get(127L).getHash()); + verify(blockchain, times(1)).setSafeBlock(byNumber.get(127L).getHash()); + assertThat(tags.finalizedNumber()).isEqualTo(127L); + } + + @Test + void startupWithNoCertifiedAnchorInTwoIntervalsSetsNothing() { + final AerePqFinalityTags tags = tagsWithHead(140); // 96 and 128 are anchor heights, none certified + verify(blockchain, never()).setFinalized(any()); + assertThat(tags.finalizedNumber()).isEqualTo(-1L); + } + + @Test + void aCertifiedAnchorAtTheHeadFinalizesItsParent() { + certified.add(96L); + final AerePqFinalityTags tags = tagsWithHead(100); // finalized 95 + certified.add(128L); + tags.onBlockAdded(headAdvanced(128)); + verify(blockchain).setFinalized(byNumber.get(127L).getHash()); + verify(blockchain).setSafeBlock(byNumber.get(127L).getHash()); + assertThat(tags.finalizedNumber()).isEqualTo(127L); + } + + @Test + void aBetweenAnchorHeadNeverMovesTheTags_thisIsWhatKeepsAForgedTailOutOfFinalized() { + certified.add(96L); + final AerePqFinalityTags tags = tagsWithHead(100); + for (long n = 101; n < 128; n++) { + certified.add(n); // even if a between-anchor header somehow "carried" a certificate + tags.onBlockAdded(headAdvanced(n)); + } + verify(blockchain, times(1)).setFinalized(any()); // only the startup one (95) + assertThat(tags.finalizedNumber()).isEqualTo(95L); + } + + @Test + void anAnchorHeightWithoutACertificateDoesNotMoveTheTags() { + certified.add(96L); + final AerePqFinalityTags tags = tagsWithHead(100); + tags.onBlockAdded(headAdvanced(128)); // 128 not certified + verify(blockchain, times(1)).setFinalized(any()); + assertThat(tags.finalizedNumber()).isEqualTo(95L); + } + + @Test + void theTagsAreMonotonic_aLowerCertifiedAnchorNeverMovesThemBack() { + certified.add(96L); + certified.add(128L); + final AerePqFinalityTags tags = tagsWithHead(140); // finalized 127 + tags.onBlockAdded(headAdvanced(96)); // certified, but lower + verify(blockchain, times(1)).setFinalized(any()); + assertThat(tags.finalizedNumber()).isEqualTo(127L); + } + + @Test + void aReorgBelowTheFinalizedBlockIsNotFollowedBackwards() { + certified.add(96L); + certified.add(128L); + final AerePqFinalityTags tags = tagsWithHead(140); // finalized 127 + tags.onBlockAdded(reorgTo(130, 100)); // common ancestor 100 < 127: logged, not moved + verify(blockchain, times(1)).setFinalized(any()); + assertThat(tags.finalizedNumber()).isEqualTo(127L); + } + + @Test + void aNonCanonicalEventIsIgnored() { + certified.add(96L); + certified.add(128L); + final AerePqFinalityTags tags = tagsWithHead(100); // finalized 95 + final BlockAddedEvent fork = mock(BlockAddedEvent.class); + when(fork.isNewCanonicalHead()).thenReturn(false); + when(fork.getHeader()).thenReturn(byNumber.get(128L)); + tags.onBlockAdded(fork); + assertThat(tags.finalizedNumber()).isEqualTo(95L); + } + + @Test + void headersAreLinkedByParentHashInTheFixture() { + assertThat(byNumber.get(128L).getParentHash()).isEqualTo(byNumber.get(127L).getHash()); + assertThat(byNumber.get(127L).getHash().size()).isEqualTo(32); + } + + // --- the certificate predicate: the controller feeds carriesCertificate(decode(header)) into install(), + // so it decides which anchors move the tags. On a stage-5 chain the certificate is the v2 (hybrid) list + // and getFalconSeals() is empty; a predicate that read only the v1 list never fired (rep28098 rehearsal, + // 2026-09-26). These three cases are the control that both fields are read. + + private static BftExtraData extraWith(final List v1, final List v2) { + return new BftExtraData( + Bytes.EMPTY, Collections.emptyList(), Optional.empty(), 0, Collections.emptyList(), v1, v2); + } + + @Test + void carriesCertificate_trueForAV1FalconList() { + assertThat( + AerePqFinalityTags.carriesCertificate( + extraWith(List.of(new FalconSeal(0, Bytes.of(1, 2, 3))), Collections.emptyList()))) + .isTrue(); + } + + @Test + void carriesCertificate_trueForAV2HybridCertificate_thisIsTheStage5AnchorThatWasBeingMissed() { + assertThat( + AerePqFinalityTags.carriesCertificate( + extraWith( + Collections.emptyList(), + List.of(new SchemeSeal((byte) 1, 0, Bytes.of(1, 2, 3)))))) + .isTrue(); + } + + @Test + void carriesCertificate_falseWhenNeitherListIsPresent() { + assertThat( + AerePqFinalityTags.carriesCertificate( + extraWith(Collections.emptyList(), Collections.emptyList()))) + .isFalse(); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentityTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentityTest.java new file mode 100644 index 0000000000..a375f42609 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/AerePqIdentityTest.java @@ -0,0 +1,135 @@ +/* + * AERE AIP-22 stage 3: probes of the identity translation (AerePqIdentity), on the registry v3 + * conformance vector ok-epoch-a (three rows, each with its ecdsa link, row 0 with a transport). + * + * What must hold, each with its negative: disarmed, every function is the identity (the build that + * carries the class and is never configured is byte for byte the old one); armed, an address becomes + * its id exactly from H_id and not one block earlier; an address without a row stays itself (so its + * message is simply not in the set); the set after H_id - 1 becomes the sorted ids and a member + * without a row REFUSES instead of being dropped; the local node is recognised in both forms and only + * its own; ids reach peers through their transport, and an id with no transport means "every peer". + */ +package org.hyperledger.besu.consensus.common.bft; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.datatypes.Address; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AerePqIdentityTest { + + private static final long H_ID = 1500; + private PqRegistryV3.Registry epoch; + private final List
    addr = new ArrayList<>(); + private final List
    id = new ArrayList<>(); + + @BeforeEach + public void armOnTheVector() throws IOException { + final JsonNode v; + try (InputStream in = getClass().getResourceAsStream("/aere/registry-v3/ok-epoch-a.json")) { + assertThat(in).isNotNull(); + v = new ObjectMapper().readTree(in); + } + epoch = + PqRegistryV3.load( + v.get("manifest"), v.get("chainId").asLong(), + List.of("falcon-512", "slh-dsa-sha2-128s"), null, true); + for (final PqRegistryV3.Row r : epoch.rows()) { + addr.add(Address.wrap(Bytes.wrap(r.ecdsaAddress()))); + id.add(Address.wrap(Bytes.wrap(r.id()))); + } + final TreeMap epochs = new TreeMap<>(); + epochs.put(epoch.bindHeight(), epoch); + AerePqIdentity.uninstall(); + assertThat(epoch.bindHeight()).isLessThanOrEqualTo(H_ID); + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs)); + } + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + @Test + public void disarmedEverythingIsTheIdentity() { + AerePqIdentity.uninstall(); + assertThat(AerePqIdentity.author(addr.get(0), H_ID + 10)).isEqualTo(addr.get(0)); + final Collection
    set = new java.util.TreeSet<>(addr); + assertThat(AerePqIdentity.translateSetAfter(set, H_ID - 1)).isSameAs(set); + assertThat(AerePqIdentity.isLocal(id.get(0), addr.get(0))).isFalse(); + assertThat(AerePqIdentity.transportTargets(addr)).contains(addr); + } + + @Test + public void anAddressBecomesItsIdExactlyFromHid() { + assertThat(AerePqIdentity.author(addr.get(1), H_ID - 1)).isEqualTo(addr.get(1)); + assertThat(AerePqIdentity.author(addr.get(1), H_ID)).isEqualTo(id.get(1)); + assertThat(AerePqIdentity.author(addr.get(2), H_ID + 1000)).isEqualTo(id.get(2)); + // an address the registry does not link stays itself: its message is then not in the set + final Address stranger = Address.fromHexString("0x00000000000000000000000000000000000000aa"); + assertThat(AerePqIdentity.author(stranger, H_ID + 5)).isEqualTo(stranger); + } + + @Test + public void theSetAfterHidMinusOneBecomesTheSortedIdsAndNowhereElse() { + final Collection
    set = new java.util.TreeSet<>(addr); + assertThat(AerePqIdentity.translateSetAfter(set, H_ID - 2)).isSameAs(set); + assertThat(AerePqIdentity.translateSetAfter(set, H_ID)).isSameAs(set); + final Collection
    t = AerePqIdentity.translateSetAfter(set, H_ID - 1); + assertThat(t).containsExactlyElementsOf(new java.util.TreeSet<>(id)); + } + + @Test + public void aMemberWithoutARowRefusesInsteadOfBeingDropped() { + final Collection
    set = new java.util.TreeSet<>(addr); + set.add(Address.fromHexString("0x00000000000000000000000000000000000000bb")); + assertThatThrownBy(() -> AerePqIdentity.translateSetAfter(set, H_ID - 1)) + .isInstanceOf(AerePqIdentity.IdentityException.class) + .hasMessageContaining("has no row"); + } + + @Test + public void theLocalNodeIsRecognisedInBothFormsAndOnlyItsOwn() { + assertThat(AerePqIdentity.isLocal(addr.get(0), addr.get(0))).isTrue(); + assertThat(AerePqIdentity.isLocal(id.get(0), addr.get(0))).isTrue(); + assertThat(AerePqIdentity.isLocal(id.get(1), addr.get(0))).isFalse(); + assertThat(AerePqIdentity.local(addr.get(0), H_ID - 1)).isEqualTo(addr.get(0)); + assertThat(AerePqIdentity.local(addr.get(0), H_ID)).isEqualTo(id.get(0)); + } + + @Test + public void idsReachPeersThroughTheirTransportOrEveryPeer() { + final byte[] t0 = epoch.rows().get(0).transport(); + assertThat(t0).isNotNull(); + assertThat(AerePqIdentity.transportTargets(List.of(id.get(0)))) + .contains(List.of(Address.wrap(Bytes.wrap(t0)))); + assertThat(epoch.rows().get(1).transport()).isNull(); + assertThat(AerePqIdentity.transportTargets(List.of(id.get(0), id.get(1)))).isEqualTo(Optional.empty()); + // below H_id the recipients are addresses and go out as before + assertThat(AerePqIdentity.transportTargets(addr)).contains(addr); + } + + @Test + public void anIdentityHeightWithNoEpochAtOrBelowItRefusesToArm() { + final TreeMap epochs = new TreeMap<>(); + epochs.put(epoch.bindHeight(), epoch); + assertThatThrownBy( + () -> AerePqIdentity.install(new AerePqIdentity.Config(epoch.bindHeight() - 1, epochs))) + .isInstanceOf(AerePqIdentity.IdentityException.class); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducerTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducerTest.java index 5569abeb7b..6dfa922ba4 100755 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducerTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSealProducerTest.java @@ -75,40 +75,69 @@ class HybridSealProducerTest { @Test void theGateAloneSuppressesEmissionEvenWhenTheScheduleDemandsIt() { final HybridSealProducer poartaInchisa = new HybridSealProducer( H_HIBRID + 100, orar, INDEX, Map.of(SealSchemes.SLH_DSA_128S.id(), slh.privateKey())); assertThat(poartaInchisa.sealsFor(H_HIBRID, MESSAGE)).isEmpty(); assertThat(poartaInchisa.sealsFor(H_HIBRID + 100, MESSAGE)).hasSize(1); } @Test void atTheHybridStepTheExtraSealIsProduced() { final List seals = producator().sealsFor(H_HIBRID, MESSAGE); assertThat(seals).hasSize(1); assertThat(seals.get(0).getSchemeWireId()).isEqualTo(SealSchemes.SLH_DSA_128S.wireId()); assertThat(seals.get(0).getValidatorIndex()).isEqualTo(INDEX); } + // ------------------------------------------- intervale per schema (AIP-22 treapta 2) + + /** + * With SLH-DSA on the 128 grid and the anchor grids counted from H0 = 64, the producer signs the + * SLH-DSA seal only at the parent of an anchor on that grid; at the parent of a Falcon-only + * anchor (32 grid) it emits nothing, although the key is there and the gate is open. The two + * heights differ in nothing but the grid, which is the only thing this proof is about. + */ + @Test + void theSlhDsaSealIsProducedOnlyAtParentsOfAnchorsOnItsOwnGrid() { + final long h0 = 64L; + final PqSchemeSchedule cuIntervale = + PqSchemeSchedule.parse( + "0:" + SealSchemes.FALCON_512.id() + "/32+" + SealSchemes.SLH_DSA_128S.id() + "/128"); + final HybridSealProducer p = + new HybridSealProducer( + H_ATASARE, cuIntervale, INDEX, Map.of(SealSchemes.SLH_DSA_128S.id(), slh.privateKey()), h0); + final long ancoraHibrida = h0 + 128L * 10; // 1344: pe grila 128 + final long ancoraDoarFalcon = ancoraHibrida + 32L; // 1376: pe grila 32, nu pe 128 + assertThat(p.sealsFor(ancoraHibrida - 1, MESSAGE)).hasSize(1); + assertThat(p.sealsFor(ancoraDoarFalcon - 1, MESSAGE)).isEmpty(); + // the negative control of the grid origin: the same heights read against another origin flip + final HybridSealProducer altaOrigine = + new HybridSealProducer( + H_ATASARE, cuIntervale, INDEX, Map.of(SealSchemes.SLH_DSA_128S.id(), slh.privateKey()), h0 + 32); + assertThat(altaOrigine.sealsFor(ancoraHibrida - 1, MESSAGE)).isEmpty(); + assertThat(altaOrigine.sealsFor(ancoraDoarFalcon - 1, MESSAGE)).hasSize(1); + } + // ---------------------------------------------------------------- jumatatea de certificat @Test void aMissingKeyEmitsNothingAtAllRatherThanAStubCertificate() { final HybridSealProducer withoutKey = new HybridSealProducer(H_ATASARE, orar, INDEX, Map.of()); assertThat(withoutKey.sealsFor(H_HIBRID, MESSAGE)).isEmpty(); } @Test void aNullMessageIsRefusedWithoutThrowing() { assertThat(producator().sealsFor(H_HIBRID, null)).isEmpty(); } // ---------------------------------------------------------------- dus-intorsul cheii @Test void theSlhDsaPrivateKeySurvivesSerializationAndStillSigns() { final byte[] encoded = SealSchemes.SLH_DSA_128S.serializePrivateKey(slh.privateKey()).orElseThrow(); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSignerRegistryTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSignerRegistryTest.java index 87bca049e7..e2e7be5948 100755 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSignerRegistryTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/HybridSignerRegistryTest.java @@ -117,64 +117,64 @@ class HybridSignerRegistryTest { p.setProperty( "7.key." + FALCON, Bytes.wrap(SealSchemes.FALCON_512.generate(random).publicRegistryForm()).toHexString()); assertThatThrownBy(() -> HybridSignerRegistry.fromProperties(p, "test")) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("beyond count"); } @Test void wrongFormatVersionRefuses() { final Properties p = sanatos(); p.setProperty("formatVersion", "hybrid-9"); assertThatThrownBy(() -> HybridSignerRegistry.fromProperties(p, "test")) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("hybrid-1"); } @Test void unrecognisedEntryRefuses() { final Properties p = sanatos(); - p.setProperty("1.cheie", "0x1234"); // aproape corect, dar nu e nici addr nici key. + p.setProperty("1.cheie", "0x1234"); // almost right, but it is neither addr nor key. assertThatThrownBy(() -> HybridSignerRegistry.fromProperties(p, "test")) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("unrecognised"); } // ------------------------------------------------------------------ the canonical hash @Test void hashIsDeterministicAndMovesWithEveryBoundThing() { final Properties p = sanatos(); final HybridSignerRegistry a = HybridSignerRegistry.fromProperties(p, "a"); final HybridSignerRegistry b = HybridSignerRegistry.fromProperties(p, "b"); assertThat(a.canonicalHash()).isEqualTo(b.canonicalHash()); // determinist - // schimb O cheie: hash-ul se misca + // change ONE key: the hash moves final Properties altKey = sanatos(); altKey.setProperty( "2.key." + SLHDSA, Bytes.wrap(SealSchemes.SLH_DSA_128S.generate(random).publicRegistryForm()).toHexString()); assertThat(HybridSignerRegistry.fromProperties(altKey, "c").canonicalHash()) .isNotEqualTo(a.canonicalHash()); - // scot o schema de la un validator: hash-ul se misca + // remove one scheme from a validator: the hash moves final Properties altScheme = sanatos(); altScheme.remove("2.key." + SLHDSA); assertThat(HybridSignerRegistry.fromProperties(altScheme, "d").canonicalHash()) .isNotEqualTo(a.canonicalHash()); // alt chainId: hash-ul se misca final Properties altChain = sanatos(); altChain.setProperty("chainId", "2801"); assertThat(HybridSignerRegistry.fromProperties(altChain, "e").canonicalHash()) .isNotEqualTo(a.canonicalHash()); } @Test void hashDomainCanNeverCollideWithTheFalconOnlyRegistryFamily() { // the domain is part of the preimage; if someone aligned it with the old family, a hybrid // registry could pass itself off as the genesis-bound v1 registry. The constant is // verified here so it cannot drift silently. assertThat(HybridSignerRegistry.HASH_DOMAIN).isEqualTo("AERE-PQ-HYBRID-REGISTRY-1"); assertThat(HybridSignerRegistry.HASH_DOMAIN).isNotEqualTo(PqRegistryHash.DOMAIN_V1); assertThat(HybridSignerRegistry.HASH_DOMAIN).isNotEqualTo(PqRegistryHash.DOMAIN_V2); @@ -183,41 +183,41 @@ class HybridSignerRegistryTest { // --------------------------------------------- the registry + the v2 certificate, together @Test void endToEndCertificateVerifiesAgainstRegistryKeysPerScheme() { final byte[] message = "commit hash stand-in, 32 bytes!!".getBytes(java.nio.charset.StandardCharsets.UTF_8); // build the registry and KEEP the private test handles so I can sign final Properties p = new Properties(); p.setProperty("formatVersion", "hybrid-1"); p.setProperty("chainId", "2800"); p.setProperty("count", "2"); final SealScheme.GeneratedPair f0 = SealSchemes.FALCON_512.generate(random); final SealScheme.GeneratedPair s0 = SealSchemes.SLH_DSA_128S.generate(random); final SealScheme.GeneratedPair f1 = SealSchemes.FALCON_512.generate(random); p.setProperty("0.addr", "0x" + "aa".repeat(20)); p.setProperty("1.addr", "0x" + "bb".repeat(20)); p.setProperty("0.key." + FALCON, Bytes.wrap(f0.publicRegistryForm()).toHexString()); p.setProperty("0.key." + SLHDSA, Bytes.wrap(s0.publicRegistryForm()).toHexString()); p.setProperty("1.key." + FALCON, Bytes.wrap(f1.publicRegistryForm()).toHexString()); final HybridSignerRegistry reg = HybridSignerRegistry.fromProperties(p, "test"); - // certificatul hibrid: validatorul 0 cu amandoua schemele, 1 doar Falcon + // the hybrid certificate: validator 0 with both schemes, 1 with Falcon only final java.util.List cert = java.util.List.of( new SchemeSeal((byte) 0x01, 0, Bytes.wrap( SealSchemes.FALCON_512.sign(f0.privateKey(), message).orElseThrow())), new SchemeSeal((byte) 0x02, 0, Bytes.wrap( SealSchemes.SLH_DSA_128S.sign(s0.privateKey(), message).orElseThrow())), new SchemeSeal((byte) 0x01, 1, Bytes.wrap( SealSchemes.FALCON_512.sign(f1.privateKey(), message).orElseThrow()))); // round-trip through the v2 format, then EACH seal against ITS OWN key from the registry for (final SchemeSeal seal : PqAnchorV2.decode(PqAnchorV2.encode(cert))) { final SealScheme scheme = SealSchemes.byWireId(seal.getSchemeWireId()).orElseThrow(); final byte[] key = reg.publicKey(seal.getValidatorIndex(), scheme.id()).orElseThrow(); assertThat(scheme.verifyRaw(key, message, seal.getSignature().toArray())) .as("sigiliul %s contra cheii lui din registru", seal) .isTrue(); } // the per-scheme threshold, on the same certificate: 2 Falcon validators, 1 SLH-DSA assertThat(PqAnchorV2.distinctValidatorsWith(cert, (byte) 0x01)).isEqualTo(2); assertThat(PqAnchorV2.distinctValidatorsWith(cert, (byte) 0x02)).isEqualTo(1); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapseTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapseTest.java old mode 100755 new mode 100644 index 038d862876..2bd1894938 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapseTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorLapseTest.java @@ -76,121 +76,121 @@ public class PqAnchorLapseTest { } @Test public void theStatedSpansAgreeWithTheBounds() { // 36, not 37: (13,268,944 - 13,267,824) / 32 + 1 = 36. And 19,425 for the second range. The // span is carried in each entry so the two cannot drift apart in silence; this asserts the // arithmetic from the outside as well. for (final PqAnchorLapse.Window window : PqAnchorLapse.windows()) { assertThat((window.lastBlock() - window.firstBlock()) % PqAnchorLapse.ANCHOR_SPACING).isZero(); assertThat((window.lastBlock() - window.firstBlock()) / PqAnchorLapse.ANCHOR_SPACING + 1L) .isEqualTo(window.anchorHeights()); } } // ----------------------------------------------------------------------------------------------- // isDisarmed: TRUE only in the first range, and deliberately FALSE in the second. // ----------------------------------------------------------------------------------------------- @Test public void isDisarmedIsTrueOnlyWhereNothingWasInForce() { - assertThat(PqAnchorLapse.isDisarmed(DISARMED_FIRST)).isTrue(); - assertThat(PqAnchorLapse.isDisarmed(DISARMED_LAST)).isTrue(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, DISARMED_FIRST)).isTrue(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, DISARMED_LAST)).isTrue(); // The neighbouring anchor heights, both measured to carry a digest, stay under the strict rules. - assertThat(PqAnchorLapse.isDisarmed(LAST_ENFORCED_BEFORE)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(DISARMED_FIRST - 1L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(DISARMED_LAST + 1L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, LAST_ENFORCED_BEFORE)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, DISARMED_FIRST - 1L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, DISARMED_LAST + 1L)).isFalse(); // And FALSE across the whole second range: there the digest and the ordering did hold, and a // relaxation that leaked into it would throw away the binding on 19,425 anchor heights. - assertThat(PqAnchorLapse.isDisarmed(LOWERED_FIRST)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(13_500_016L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(LOWERED_LAST)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(FIRST_FULLY_ENFORCED_AFTER)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, 13_500_016L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, LOWERED_LAST)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, FIRST_FULLY_ENFORCED_AFTER)).isFalse(); } @Test public void everyAnchorHeightOfTheDisarmedRangeIsCovered() { long covered = 0L; for (long height = DISARMED_FIRST; height <= DISARMED_LAST; height += PqAnchorLapse.ANCHOR_SPACING) { - assertThat(PqAnchorLapse.isDisarmed(height)) + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, height)) .describedAs("anchor height %d of the disarmed range", height) .isTrue(); covered++; } assertThat(covered).isEqualTo(36L); // The rest of the chain is untouched. - assertThat(PqAnchorLapse.isDisarmed(0L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(13_014_000L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(13_000_000L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(14_077_000L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(Long.MAX_VALUE)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, 0L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, 13_014_000L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, 13_000_000L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, 14_077_000L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, Long.MAX_VALUE)).isFalse(); } // ----------------------------------------------------------------------------------------------- // historicMinSeals: the threshold that was really in force, and only where it was lower. // ----------------------------------------------------------------------------------------------- @Test public void historicMinSealsAnswersOnlyInsideTheLoweredRange() { - assertThat(PqAnchorLapse.historicMinSeals(LOWERED_FIRST)).hasValue(1); - assertThat(PqAnchorLapse.historicMinSeals(13_500_016L)).hasValue(1); - assertThat(PqAnchorLapse.historicMinSeals(LOWERED_LAST)).hasValue(1); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST)).hasValue(1); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, 13_500_016L)).hasValue(1); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOWERED_LAST)).hasValue(1); // One anchor step past the end and the configured schedule applies again, unchanged. This is // the assertion that stops the lowered threshold from becoming permanent. - assertThat(PqAnchorLapse.historicMinSeals(FIRST_FULLY_ENFORCED_AFTER)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(LOWERED_LAST + 1L)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(LOWERED_FIRST - 1L)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(LAST_ENFORCED_BEFORE)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(14_077_000L)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(Long.MAX_VALUE)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, FIRST_FULLY_ENFORCED_AFTER)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOWERED_LAST + 1L)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST - 1L)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LAST_ENFORCED_BEFORE)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, 14_077_000L)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, Long.MAX_VALUE)).isEmpty(); // And it does NOT answer inside the disarmed range: there the rules are skipped whole, so a // threshold would be a second, contradictory way of saying the same thing. - assertThat(PqAnchorLapse.historicMinSeals(DISARMED_FIRST)).isEmpty(); - assertThat(PqAnchorLapse.historicMinSeals(DISARMED_LAST)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, DISARMED_FIRST)).isEmpty(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, DISARMED_LAST)).isEmpty(); } @Test public void theFloorInsideTheLoweredRangeIsOneAndNotZero() { // Measured: 2,926 of the 19,425 anchor heights in that range carry fewer seals than the // schedule asks, the counts seen are 1 and 2, and NOT ONE carries zero. A floor of zero would // therefore accept an empty certificate at a height where a certificate was in fact required, // which is weaker than the history needs. - assertThat(PqAnchorLapse.historicMinSeals(LOWERED_FIRST)).hasValue(1); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST)).hasValue(1); assertThat(PqAnchorLapse.windows().get(1).effectiveMinSeals()).isPositive(); } @Test public void windowCoveringNamesTheRangeOrNothing() { - assertThat(PqAnchorLapse.windowCovering(DISARMED_FIRST)).isPresent(); - assertThat(PqAnchorLapse.windowCovering(DISARMED_FIRST).get().lastBlock()) + assertThat(PqAnchorLapse.windowCovering(PqAnchorLapse.CHAIN_ID, DISARMED_FIRST)).isPresent(); + assertThat(PqAnchorLapse.windowCovering(PqAnchorLapse.CHAIN_ID, DISARMED_FIRST).get().lastBlock()) .isEqualTo(DISARMED_LAST); - assertThat(PqAnchorLapse.windowCovering(LOWERED_FIRST)).isPresent(); - assertThat(PqAnchorLapse.windowCovering(LOWERED_FIRST).get().relaxation()) + assertThat(PqAnchorLapse.windowCovering(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST)).isPresent(); + assertThat(PqAnchorLapse.windowCovering(PqAnchorLapse.CHAIN_ID, LOWERED_FIRST).get().relaxation()) .isEqualTo(PqAnchorLapse.Relaxation.THRESHOLD_ONLY); - assertThat(PqAnchorLapse.windowCovering(FIRST_FULLY_ENFORCED_AFTER)).isEmpty(); + assertThat(PqAnchorLapse.windowCovering(PqAnchorLapse.CHAIN_ID, FIRST_FULLY_ENFORCED_AFTER)).isEmpty(); assertThat(PqAnchorLapse.earliestAffectedBlock()).contains(DISARMED_FIRST); } @Test public void theDisarmedRangeIsTheSameOBJECTTheOrderingExceptionUses() { // A second copy of these bounds anywhere else is a value that diverges. PqAnchor used to carry // them as its own literals; it now derives them, and this is the assertion that says so. assertThat(PqAnchor.UNORDERED_WINDOW_FIRST).isEqualTo(DISARMED_FIRST); assertThat(PqAnchor.UNORDERED_WINDOW_LAST).isEqualTo(DISARMED_LAST); } // ----------------------------------------------------------------------------------------------- // The self-checks can say no. A guard that has never refused anything cannot be believed. // ----------------------------------------------------------------------------------------------- @Test public void aRangeThatContradictsItselfIsRefusedRatherThanAccepted() { assertThatThrownBy( () -> new PqAnchorLapse.Window( diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorTest.java old mode 100755 new mode 100644 index 5bea4d37e3..aabecee7c2 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqAnchorTest.java @@ -162,78 +162,78 @@ public class PqAnchorTest { // Attack A4: the same index twice to inflate k. Unrepresentable, not filtered afterwards. assertThat(PqAnchor.hasStrictlyIncreasingIndices(Arrays.asList(seal(2), seal(2)))).isFalse(); assertThat( PqAnchor.hasStrictlyIncreasingIndices( Arrays.asList(new FalconSeal(2, signatureFor(2, 0)), new FalconSeal(2, signatureFor(2, 1))))) .isFalse(); assertThat(PqAnchor.hasStrictlyIncreasingIndices(List.of(new FalconSeal(-1, Bytes.of(1))))) .isFalse(); assertThat(PqAnchor.hasStrictlyIncreasingIndices(List.of(new FalconSeal(1, null)))).isFalse(); } @Test public void historicalUnorderedWindowIsAcceptedAndOnlyThere() { // The 36 canonical headers written unsorted during the 2026-08-10 interruption. Two real index // sequences, decoded off the public endpoint on 2026-08-11: block 13,267,824 lists 2, 6, 1 and // block 13,268,944 lists 5, 6, 1, 0. final List real1 = Arrays.asList(seal(2), seal(6), seal(1)); final List real2 = Arrays.asList(seal(5), seal(6), seal(1), seal(0)); // Inside the window: accepted, at both edges and in the middle. - assertThat(PqAnchor.hasAcceptableIndices(real1, PqAnchor.UNORDERED_WINDOW_FIRST)).isTrue(); - assertThat(PqAnchor.hasAcceptableIndices(real2, PqAnchor.UNORDERED_WINDOW_LAST)).isTrue(); - assertThat(PqAnchor.hasAcceptableIndices(real1, PqAnchor.UNORDERED_WINDOW_FIRST + 32L)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real1, PqAnchor.UNORDERED_WINDOW_FIRST)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real2, PqAnchor.UNORDERED_WINDOW_LAST)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real1, PqAnchor.UNORDERED_WINDOW_FIRST + 32L)).isTrue(); // NEGATIVE CONTROL, and it is the whole point: one block on either side of the window and the // very same certificate is rejected. If these two ever pass, the exception has stopped being an // exception and has become the rule. - assertThat(PqAnchor.hasAcceptableIndices(real1, PqAnchor.UNORDERED_WINDOW_FIRST - 1L)).isFalse(); - assertThat(PqAnchor.hasAcceptableIndices(real1, PqAnchor.UNORDERED_WINDOW_LAST + 1L)).isFalse(); - assertThat(PqAnchor.hasAcceptableIndices(real2, 1L)).isFalse(); - assertThat(PqAnchor.hasAcceptableIndices(real2, Long.MAX_VALUE)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real1, PqAnchor.UNORDERED_WINDOW_FIRST - 1L)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real1, PqAnchor.UNORDERED_WINDOW_LAST + 1L)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real2, 1L)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, real2, Long.MAX_VALUE)).isFalse(); } @Test public void historicalWindowDoesNotRelaxDistinctness() { // Attack A4 inside the window: repeating one seal to inflate k. Sortedness is relaxed there, // distinctness is NOT, at any height. A certificate that repeats an index is still // unrepresentable, which is the only part of the ordering rule that an attacker cares about. final List repetat = Arrays.asList(new FalconSeal(2, signatureFor(2, 0)), new FalconSeal(2, signatureFor(2, 1))); - assertThat(PqAnchor.hasAcceptableIndices(repetat, PqAnchor.UNORDERED_WINDOW_FIRST)).isFalse(); - assertThat(PqAnchor.hasAcceptableIndices(repetat, PqAnchor.UNORDERED_WINDOW_LAST)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, repetat, PqAnchor.UNORDERED_WINDOW_FIRST)).isFalse(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, repetat, PqAnchor.UNORDERED_WINDOW_LAST)).isFalse(); // Negative indices and null signatures stay rejected inside the window too. assertThat( - PqAnchor.hasAcceptableIndices( + PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, List.of(new FalconSeal(-1, Bytes.of(1))), PqAnchor.UNORDERED_WINDOW_FIRST)) .isFalse(); assertThat( - PqAnchor.hasAcceptableIndices( + PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, List.of(new FalconSeal(1, null)), PqAnchor.UNORDERED_WINDOW_FIRST)) .isFalse(); // And a sorted certificate is accepted everywhere, window or not: the exception only ever adds. final List sortat = Arrays.asList(seal(0), seal(3), seal(6)); - assertThat(PqAnchor.hasAcceptableIndices(sortat, 1L)).isTrue(); - assertThat(PqAnchor.hasAcceptableIndices(sortat, PqAnchor.UNORDERED_WINDOW_FIRST)).isTrue(); - assertThat(PqAnchor.hasAcceptableIndices(sortat, Long.MAX_VALUE)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, sortat, 1L)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, sortat, PqAnchor.UNORDERED_WINDOW_FIRST)).isTrue(); + assertThat(PqAnchor.hasAcceptableIndices(PqAnchorLapse.CHAIN_ID, sortat, Long.MAX_VALUE)).isTrue(); } @Test public void historicalWindowIsClosedAndAnchorAligned() { // The window cannot grow: it is bounded above by a measured constant. And both bounds must sit on // the 32-block anchor spacing, because only anchor heights carry certificates. A window whose // bounds are off the spacing would be evidence that the measurement, not the chain, was wrong. assertThat(PqAnchor.UNORDERED_WINDOW_LAST).isGreaterThan(PqAnchor.UNORDERED_WINDOW_FIRST); assertThat((PqAnchor.UNORDERED_WINDOW_LAST - PqAnchor.UNORDERED_WINDOW_FIRST) % 32L).isZero(); // 36 anchor headers, measured contiguous on 2026-08-11. assertThat((PqAnchor.UNORDERED_WINDOW_LAST - PqAnchor.UNORDERED_WINDOW_FIRST) / 32L + 1L) .isEqualTo(36L); } @Test public void sortedByIndexOrdersAscending() { final List sorted = PqAnchor.sortedByIndex(Arrays.asList(seal(5), seal(1), seal(3))); assertThat(sorted.stream().map(FalconSeal::getValidatorIndex)).containsExactly(1, 3, 5); assertThat(PqAnchor.hasStrictlyIncreasingIndices(sorted)).isTrue(); } diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3Test.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3Test.java new file mode 100644 index 0000000000..e5538e1c61 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqRegistryV3Test.java @@ -0,0 +1,509 @@ +/* + * AERE AIP-22 stage 3: probes of the v3 registry, and the writer of its conformance vectors. + * + * TWO MODES. With the environment variable AERE_REG3_VECTORS_OUT set to a directory, the vector + * writer generates PROBA keys (its helpers are package-visible since 2026-09-23: AereLocalKeysV3Test builds its own epochs with + * private keys it holds) (Falcon-512, SLH-DSA-SHA2-128s, secp256k1), builds two epochs and one + * negative vector per refusal code, and writes them as JSON; each negative differs from a valid + * manifest in ONE field (the lesson of AIP-20: a negative that differs in two places measures which + * check the validator reaches first, not the one it names), and where a field feeds a signed + * message the signatures are re-made over the changed field so that only the named rule can fail. + * Without the variable, every test reads the checked-in vectors from test resources + * (aere/registry-v3/) and requires the outcome each vector names: "ok" with the stated registry hash + * and row hashes, or the exact refusal code. The same files are read by the Nethermind port and by + * the public JS tool, which is what makes them conformance vectors and not just fixtures. + */ +package org.hyperledger.besu.consensus.common.bft; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SecureRandomProvider; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.core.Util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +public class PqRegistryV3Test { + + private static final ObjectMapper M = + new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + private static final String RES = "/aere/registry-v3/"; + private static final List VECTORS = + List.of( + "ok-epoch-a", "ok-epoch-b-rotation", "n01-format", "n02-chain", "n03-extra-field", + "n05-unknown-scheme", "n06-required-scheme", "n07-falcon-header", "n08-pop", + "n09-id-derivation", "n10-duplicate-id", "n11-duplicate-key", "n12-rotation-one-key", + "n12b-rotation-wrong-key", "n13-rotation-unchanged", "n14-ecdsa-wrong-key", + "n14b-ecdsa-missing", "n15-transport", "n16-declared-hash"); + + // ------------------------------------------------------------------------------------------ + // The conformance vectors + // ------------------------------------------------------------------------------------------ + + @Test + public void everyVectorHasTheOutcomeItNames() throws IOException { + int ok = 0; + int refused = 0; + for (final String name : VECTORS) { + final JsonNode v = vector(name); + final PqRegistryV3.Registry prev = + v.hasNonNull("previous") + ? loadVector(vector(v.get("previous").asText()), null) + : null; + final String expect = v.get("expect").asText(); + if ("ok".equals(expect)) { + final PqRegistryV3.Registry r = loadVector(v, prev); + assertThat(r.hash().toHexString()).as(name).isEqualTo(v.get("registryHash").asText()); + final ArrayNode rh = (ArrayNode) v.get("rowHashes"); + assertThat(r.rowHashes()).as(name).hasSize(rh.size()); + for (int i = 0; i < rh.size(); i++) { + assertThat(r.rowHashes().get(i).toHexString()).as(name + " row " + i) + .isEqualTo(rh.get(i).asText()); + } + ok++; + } else { + final PqRegistryV3.Registry prevF = prev; + assertThatThrownBy(() -> loadVector(v, prevF)) + .as(name) + .isInstanceOf(PqRegistryV3.RegistryV3Exception.class) + .hasMessageStartingWith(expect + ":"); + refused++; + } + } + assertThat(ok).isEqualTo(2); + assertThat(refused).isEqualTo(VECTORS.size() - 2); + } + + @Test + public void merkleProofsVerifyForEveryRowAndNotOneRowPastTheEnd() throws IOException { + final JsonNode v = vector("ok-epoch-a"); + final PqRegistryV3.Registry r = loadVector(v, null); + for (int i = 0; i < r.count(); i++) { + final List p = PqRegistryV3.merkleProof(r.rowHashes(), i); + assertThat(PqRegistryV3.verifyMerkleProof(r.rowHashes().get(i), i, r.count(), p, r.root())) + .as("row " + i) + .isTrue(); + } + // three rows: the last leaf is paired with itself, so its path also "proves" index 3 unless the + // count bound refuses it - that refusal is the whole point of the bound + final int last = r.count() - 1; + final List p = PqRegistryV3.merkleProof(r.rowHashes(), last); + assertThat(r.count() % 2).isEqualTo(1); + assertThat( + PqRegistryV3.verifyMerkleProof(r.rowHashes().get(last), last + 1, r.count(), p, r.root())) + .isFalse(); + // and a leaf that is not a row never verifies + assertThat( + PqRegistryV3.verifyMerkleProof( + Bytes32.fromHexStringLenient("0x01"), 0, r.count(), + PqRegistryV3.merkleProof(r.rowHashes(), 0), r.root())) + .isFalse(); + } + + @Test + public void theIdIsTheAip20SenderDerivationOfTheFalconKey() throws IOException { + final JsonNode v = vector("ok-epoch-a"); + final PqRegistryV3.Registry r = loadVector(v, null); + for (final PqRegistryV3.Row row : r.rows()) { + final byte[] pk = row.key("falcon-512").orElseThrow().publicKey(); + assertThat(pk).hasSize(897); + assertThat(pk[0]).isEqualTo((byte) 0x09); + assertThat(PqRegistryV3.idOf(pk)).isEqualTo(row.id()); + } + } + + @Test + public void theRegistryHashCommitsToChainHeightAndCount() throws IOException { + final JsonNode v = vector("ok-epoch-a"); + final PqRegistryV3.Registry r = loadVector(v, null); + final Bytes32 h = PqRegistryV3.registryHash(r.chainId(), r.bindHeight(), r.count(), r.root()); + assertThat(h).isEqualTo(r.hash()); + assertThat(PqRegistryV3.registryHash(r.chainId() + 1, r.bindHeight(), r.count(), r.root())) + .isNotEqualTo(h); + assertThat(PqRegistryV3.registryHash(r.chainId(), r.bindHeight() + 1, r.count(), r.root())) + .isNotEqualTo(h); + assertThat(PqRegistryV3.registryHash(r.chainId(), r.bindHeight(), r.count() + 1, r.root())) + .isNotEqualTo(h); + } + + @Test + public void thePinnedHashIsCompared() throws IOException { + final JsonNode v = vector("ok-epoch-a"); + final Bytes32 good = Bytes32.fromHexString(v.get("registryHash").asText()); + final PqRegistryV3.Registry r = + PqRegistryV3.loadPinned( + v.get("manifest"), v.get("chainId").asLong(), required(v), null, + v.get("ecdsaRequired").asBoolean(), good); + assertThat(r.hash()).isEqualTo(good); + assertThatThrownBy( + () -> + PqRegistryV3.loadPinned( + v.get("manifest"), v.get("chainId").asLong(), required(v), null, + v.get("ecdsaRequired").asBoolean(), Bytes32.ZERO)) + .isInstanceOf(PqRegistryV3.RegistryV3Exception.class) + .hasMessageStartingWith("AERE-PQC-REG3-16:"); + } + + private static PqRegistryV3.Registry loadVector(final JsonNode v, final PqRegistryV3.Registry prev) { + return PqRegistryV3.load( + v.get("manifest"), v.get("chainId").asLong(), required(v), prev, + v.get("ecdsaRequired").asBoolean()); + } + + private static List required(final JsonNode v) { + final List out = new ArrayList<>(); + v.get("requiredSchemes").forEach(n -> out.add(n.asText())); + return out; + } + + private static JsonNode vector(final String name) throws IOException { + try (InputStream in = PqRegistryV3Test.class.getResourceAsStream(RES + name + ".json")) { + assertThat(in).as("vector " + name + " is on the test classpath").isNotNull(); + return M.readTree(in); + } + } + + // ------------------------------------------------------------------------------------------ + // The vector writer (runs only with AERE_REG3_VECTORS_OUT) + // ------------------------------------------------------------------------------------------ + + static final long CHAIN = 28001; + private static final SecureRandom RNG = SecureRandomProvider.createSecureRandom(); + private static final SignatureAlgorithm EC = SignatureAlgorithmFactory.getInstance(); + + /** One validator's PROBA keys. */ + record Proba( + SealScheme.GeneratedPair falcon, SealScheme.GeneratedPair slh, KeyPair ecdsa, byte[] node) { + byte[] falconStd() { + final byte[] h = falcon.publicRegistryForm(); + final byte[] s = new byte[897]; + s[0] = 0x09; + System.arraycopy(h, 0, s, 1, 896); + return s; + } + + byte[] address() { + return Util.publicKeyToAddress(ecdsa.getPublicKey()).getBytes().toArrayUnsafe(); + } + } + + static Proba proba() { + final KeyPair node = EC.generateKeyPair(); + return new Proba( + SealSchemes.FALCON_512.generate(RNG), + SealSchemes.SLH_DSA_128S.generate(RNG), + EC.generateKeyPair(), + Util.publicKeyToAddress(node.getPublicKey()).getBytes().toArrayUnsafe()); + } + + /** A row under construction: keys (scheme id to pair) with the id they are filed under. */ + record Draft(byte[] id, Map keys, Proba owner) {} + + static ObjectNode rowJson( + final long bind, final int count, final int index, final Draft d, final boolean ecdsa, + final boolean transport) { + final ObjectNode row = M.createObjectNode(); + row.put("id", hx(d.id())); + final ObjectNode keys = row.putObject("keys"); + final ObjectNode pop = row.putObject("pop"); + for (final Map.Entry e : d.keys().entrySet()) { + final SealScheme s = SealSchemes.byId(e.getKey()).orElseThrow(); + final byte[] std = standard(s, e.getValue()); + keys.put(e.getKey(), hx(std)); + final Bytes32 msg = PqRegistryV3.popMessage(CHAIN, bind, count, index, d.id(), s.wireId(), std); + pop.put(e.getKey(), hx(s.sign(e.getValue().privateKey(), msg.toArrayUnsafe()).orElseThrow())); + } + if (transport) { + row.putObject("transport").put("secp256k1Address", hx(d.owner().node())); + } + if (ecdsa) { + claim(row, bind, count, index, d.owner().ecdsa()); + } + return row; + } + + /** (Re)computes the ecdsa claim of a row from its current JSON, with the given key. */ + private static void claim( + final ObjectNode row, final long bind, final int count, final int index, final KeyPair kp) { + final Bytes32 rh = rowHashOf(row, bind, count, index); + final Bytes32 d = PqRegistryV3.claimDigest(CHAIN, rh); + final ObjectNode ec = row.putObject("ecdsa"); + ec.put("addr", hx(Util.publicKeyToAddress(kp.getPublicKey()).getBytes().toArrayUnsafe())); + ec.put("claim", hx(EC.sign(d, kp).encodedBytes().toArrayUnsafe())); + } + + /** The row hash of a row as written in JSON (reusing the loader's own parser via a 1-row load + * is impossible here, so the pre-image is rebuilt from the JSON fields). */ + private static Bytes32 rowHashOf(final ObjectNode row, final long bind, final int count, final int index) { + final TreeMap keys = new TreeMap<>(); + final Iterator> it = row.get("keys").fields(); + while (it.hasNext()) { + final Map.Entry e = it.next(); + final SealScheme s = SealSchemes.byId(e.getKey()).orElseThrow(); + keys.put( + s.wireId() & 0xff, + new PqRegistryV3.Key( + s, Bytes.fromHexString(e.getValue().asText()).toArrayUnsafe(), + Bytes.fromHexString(row.get("pop").get(e.getKey()).asText()).toArrayUnsafe())); + } + final byte[] transport = + row.has("transport") + ? Bytes.fromHexString(row.get("transport").get("secp256k1Address").asText()).toArrayUnsafe() + : null; + final PqRegistryV3.Row r = + new PqRegistryV3.Row( + index, Bytes.fromHexString(row.get("id").asText()).toArrayUnsafe(), keys, transport, + Map.of(), null, null); + return PqRegistryV3.rowHash(CHAIN, bind, count, r); + } + + private static byte[] standard(final SealScheme s, final SealScheme.GeneratedPair p) { + if (s == SealSchemes.FALCON_512) { + final byte[] h = p.publicRegistryForm(); + final byte[] std = new byte[897]; + std[0] = 0x09; + System.arraycopy(h, 0, std, 1, 896); + return std; + } + return p.publicRegistryForm(); + } + + static ObjectNode manifest(final long bind, final List rows) { + final ObjectNode m = M.createObjectNode(); + m.put("formatVersion", 3); + m.put("chainId", CHAIN); + m.put("bindHeight", bind); + m.put("count", rows.size()); + for (int i = 0; i < rows.size(); i++) { + m.set(Integer.toString(i), rows.get(i)); + } + return m; + } + + private static ObjectNode wrap( + final String description, final ObjectNode manifest, final boolean ecdsaRequired, + final String previous, final String expect) { + final ObjectNode v = M.createObjectNode(); + v.put("description", description); + v.put("chainId", CHAIN); + final ArrayNode req = v.putArray("requiredSchemes"); + req.add("falcon-512"); + req.add("slh-dsa-sha2-128s"); + v.put("ecdsaRequired", ecdsaRequired); + if (previous == null) { + v.putNull("previous"); + } else { + v.put("previous", previous); + } + v.put("expect", expect); + v.set("manifest", manifest); + return v; + } + + static Map both(final Proba p) { + final Map m = new java.util.LinkedHashMap<>(); + m.put("falcon-512", p.falcon()); + m.put("slh-dsa-sha2-128s", p.slh()); + return m; + } + + @Test + @EnabledIfEnvironmentVariable(named = "AERE_REG3_VECTORS_OUT", matches = ".+") + public void writeTheConformanceVectors() throws IOException { + final Path out = Path.of(System.getenv("AERE_REG3_VECTORS_OUT")); + Files.createDirectories(out); + final Proba v0 = proba(); + final Proba v1 = proba(); + final Proba v2 = proba(); + final Proba v3 = proba(); + final long bindA = 1000; + final long bindB = 2000; + final Draft d0 = new Draft(PqRegistryV3.idOf(v0.falconStd()), both(v0), v0); + final Draft d1 = new Draft(PqRegistryV3.idOf(v1.falconStd()), both(v1), v1); + final Draft d2 = new Draft(PqRegistryV3.idOf(v2.falconStd()), both(v2), v2); + + // epoch A: three rows, the switch epoch (ecdsa on every row), transport on row 0 + final List a = new ArrayList<>(); + a.add(rowJson(bindA, 3, 0, d0, true, true)); + a.add(rowJson(bindA, 3, 1, d1, true, false)); + a.add(rowJson(bindA, 3, 2, d2, true, false)); + final ObjectNode ma = manifest(bindA, a); + final ObjectNode okA = wrap("epoch A: 3 rows, falcon-512 + slh-dsa-sha2-128s, the switch epoch", ma, true, null, "ok"); + final PqRegistryV3.Registry ra = PqRegistryV3.load(ma, CHAIN, List.of("falcon-512", "slh-dsa-sha2-128s"), null, true); + addHashes(okA, ra); + write(out, "ok-epoch-a", okA); + + // epoch B: row 1 ROTATES both keys (keeps its id), row 2 unchanged, a new validator joins, v0 leaves + final Proba v1b = proba(); + final Draft d1b = new Draft(d1.id(), both(v1b), v1); + final Draft d3 = new Draft(PqRegistryV3.idOf(v3.falconStd()), both(v3), v3); + final List b = new ArrayList<>(); + b.add(rowJson(bindB, 3, 0, d2, false, false)); + final ObjectNode r1b = rowJson(bindB, 3, 1, d1b, false, false); + rotate(r1b, bindB, 3, 1, List.of(v1.falcon(), v1.slh())); + b.add(r1b); + b.add(rowJson(bindB, 3, 2, d3, false, false)); + final ObjectNode mb = manifest(bindB, b); + final ObjectNode okB = wrap("epoch B after A: index 1 rotates both keys under its id, index 0 unchanged (moved), a new id joins", mb, false, "ok-epoch-a", "ok"); + final PqRegistryV3.Registry rb = PqRegistryV3.load(mb, CHAIN, List.of("falcon-512", "slh-dsa-sha2-128s"), ra, false); + addHashes(okB, rb); + write(out, "ok-epoch-b-rotation", okB); + + // negatives on epoch A, one field each + ObjectNode m = ma.deepCopy(); + m.put("formatVersion", 2); + write(out, "n01-format", wrap("formatVersion 2", m, true, null, "AERE-PQC-REG3-01")); + m = ma.deepCopy(); + m.put("chainId", 2800); + write(out, "n02-chain", wrap("chainId of another chain", m, true, null, "AERE-PQC-REG3-02")); + m = ma.deepCopy(); + m.put("3", M.createObjectNode()); + write(out, "n03-extra-field", wrap("a row \"3\" beyond count 3", m, true, null, "AERE-PQC-REG3-03")); + m = ma.deepCopy(); + ((ObjectNode) m.get("1").get("keys")).put("ml-dsa-65", "0x00"); + ((ObjectNode) m.get("1").get("pop")).put("ml-dsa-65", "0x00"); + write(out, "n05-unknown-scheme", wrap("a key of a scheme this build does not know", m, true, null, "AERE-PQC-REG3-05")); + // n06: row 2 without its SLH-DSA key; pop and claim re-made so only the missing scheme can fail + { + final Map onlyF = new java.util.LinkedHashMap<>(); + onlyF.put("falcon-512", v2.falcon()); + final List x = new ArrayList<>(a); + x.set(2, rowJson(bindA, 3, 2, new Draft(d2.id(), onlyF, v2), true, false)); + write(out, "n06-required-scheme", wrap("row 2 carries no slh-dsa key", manifest(bindA, x), true, null, "AERE-PQC-REG3-06")); + } + m = ma.deepCopy(); + final String fk = m.get("0").get("keys").get("falcon-512").asText(); + ((ObjectNode) m.get("0").get("keys")).put("falcon-512", "0x0a" + fk.substring(4)); + write(out, "n07-falcon-header", wrap("row 0 falcon key with header 0x0a instead of 0x09", m, true, null, "AERE-PQC-REG3-07")); + m = ma.deepCopy(); + ((ObjectNode) m.get("1").get("pop")).put("falcon-512", m.get("0").get("pop").get("falcon-512").asText()); + write(out, "n08-pop", wrap("row 1 carries row 0's falcon possession proof", m, true, null, "AERE-PQC-REG3-08")); + // n09: row 1 filed under an id that is not its derivation; every proof re-made over that id + { + final byte[] wrongId = new byte[20]; + RNG.nextBytes(wrongId); + final List x = new ArrayList<>(a); + x.set(1, rowJson(bindA, 3, 1, new Draft(wrongId, both(v1), v1), true, false)); + write(out, "n09-id-derivation", wrap("row 1 id is not keccak(0x50||0x20||falcon)[12:]", manifest(bindA, x), true, null, "AERE-PQC-REG3-09")); + } + // n10: row 2 filed under row 1's id; proofs re-made + { + final List x = new ArrayList<>(a); + x.set(2, rowJson(bindA, 3, 2, new Draft(d1.id(), both(v2), v2), true, false)); + write(out, "n10-duplicate-id", wrap("row 2 carries row 1's id", manifest(bindA, x), true, null, "AERE-PQC-REG3-10")); + } + // n11: row 2 carries row 1's SLH-DSA key (its proof made by that key, over row 2) + { + final Map k = new java.util.LinkedHashMap<>(); + k.put("falcon-512", v2.falcon()); + k.put("slh-dsa-sha2-128s", v1.slh()); + final List x = new ArrayList<>(a); + x.set(2, rowJson(bindA, 3, 2, new Draft(d2.id(), k, v2), true, false)); + write(out, "n11-duplicate-key", wrap("row 2 carries row 1's slh-dsa key", manifest(bindA, x), true, null, "AERE-PQC-REG3-11")); + } + // n15: transport of 19 bytes (claim re-made over the row as written is impossible: the loader + // refuses the field before hashing, which is the point) + m = ma.deepCopy(); + ((ObjectNode) m.get("0").get("transport")).put("secp256k1Address", "0x" + "11".repeat(19)); + write(out, "n15-transport", wrap("row 0 transport is 19 bytes", m, true, null, "AERE-PQC-REG3-15")); + // n14: row 1's claim made by row 2's classical key; n14b: row 1 without the link in the switch epoch + { + final ObjectNode x1 = a.get(1).deepCopy(); + claim(x1, bindA, 3, 1, v2.ecdsa()); + ((ObjectNode) x1.get("ecdsa")).put("addr", hx(v1.address())); + final List x = new ArrayList<>(a); + x.set(1, x1); + write(out, "n14-ecdsa-wrong-key", wrap("row 1 claim signed by another validator's key", manifest(bindA, x), true, null, "AERE-PQC-REG3-14")); + final ObjectNode y1 = a.get(1).deepCopy(); + y1.remove("ecdsa"); + final List y = new ArrayList<>(a); + y.set(1, y1); + write(out, "n14b-ecdsa-missing", wrap("row 1 has no ecdsa link in the switch epoch", manifest(bindA, y), true, null, "AERE-PQC-REG3-14")); + } + m = ma.deepCopy(); + m.put("registryHash", "0x" + "00".repeat(32)); + write(out, "n16-declared-hash", wrap("the file declares a registry hash it does not have", m, true, null, "AERE-PQC-REG3-16")); + + // negatives on epoch B + { + final ObjectNode x1 = rowJson(bindB, 3, 1, d1b, false, false); + rotate(x1, bindB, 3, 1, List.of(v1.falcon())); + final List x = new ArrayList<>(b); + x.set(1, x1); + write(out, "n12-rotation-one-key", wrap("index 1 rotates with the old falcon key only (slh missing)", manifest(bindB, x), false, "ok-epoch-a", "AERE-PQC-REG3-12")); + final ObjectNode z1 = rowJson(bindB, 3, 1, d1b, false, false); + rotate(z1, bindB, 3, 1, List.of(v3.falcon(), v1.slh())); + final List z = new ArrayList<>(b); + z.set(1, z1); + write(out, "n12b-rotation-wrong-key", wrap("index 1 rotation's falcon signature is by another validator's key", manifest(bindB, z), false, "ok-epoch-a", "AERE-PQC-REG3-12")); + final ObjectNode u0 = rowJson(bindB, 3, 0, d2, false, false); + rotate(u0, bindB, 3, 0, List.of(v2.falcon(), v2.slh())); + final List u = new ArrayList<>(b); + u.set(0, u0); + write(out, "n13-rotation-unchanged", wrap("index 0 did not change its keys and carries a rotation", manifest(bindB, u), false, "ok-epoch-a", "AERE-PQC-REG3-13")); + } + } + + /** Adds rotation signatures over the row's current hash, one per given old key, in scheme order. */ + static void rotate( + final ObjectNode row, final long bind, final int count, final int index, + final List oldKeys) { + final Bytes32 msg = PqRegistryV3.rotateMessage(rowHashOf(row, bind, count, index)); + final ObjectNode rot = row.putObject("rotation"); + final String[] names = {"falcon-512", "slh-dsa-sha2-128s"}; + for (int i = 0; i < oldKeys.size(); i++) { + final SealScheme s = SealSchemes.byId(names[i]).orElseThrow(); + rot.put(names[i], hx(s.sign(oldKeys.get(i).privateKey(), msg.toArrayUnsafe()).orElseThrow())); + } + } + + private static void addHashes(final ObjectNode v, final PqRegistryV3.Registry r) { + v.put("registryHash", r.hash().toHexString()); + v.put("root", r.root().toHexString()); + final ArrayNode rh = v.putArray("rowHashes"); + r.rowHashes().forEach(h -> rh.add(h.toHexString())); + final ObjectNode proofs = v.putObject("merkleProofs"); + for (int i = 0; i < r.count(); i++) { + final ArrayNode p = proofs.putArray(Integer.toString(i)); + PqRegistryV3.merkleProof(r.rowHashes(), i).forEach(h -> p.add(h.toHexString())); + } + final ArrayNode ids = v.putArray("ids"); + r.rows().forEach(row -> ids.add(hx(row.id()))); + } + + private static void write(final Path dir, final String name, final JsonNode v) throws IOException { + Files.writeString(dir.resolve(name + ".json"), M.writeValueAsString(v) + "\n", StandardCharsets.UTF_8); + } + + private static String hx(final byte[] b) { + final StringBuilder sb = new StringBuilder("0x"); + for (final byte x : b) { + sb.append(String.format(Locale.ROOT, "%02x", x & 0xff)); + } + return sb.toString(); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqSchemeScheduleTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqSchemeScheduleTest.java index 41e03e825e..41f03ceda2 100755 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqSchemeScheduleTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/PqSchemeScheduleTest.java @@ -60,68 +60,120 @@ class PqSchemeScheduleTest { .isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> PqSchemeSchedule.parse("100:")) .isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> PqSchemeSchedule.parse("100:falcon-512+falcon-512")) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("repeats"); assertThatThrownBy(() -> PqSchemeSchedule.parse("-5:falcon-512")) .isInstanceOf(IllegalArgumentException.class); } // ------------------------------------------------------------- poarta de armare (D-147) private HybridSignerRegistry registruCuAcoperire(final int falconi, final int slhuri) { final Properties p = new Properties(); final int count = Math.max(falconi, Math.max(slhuri, 1)); p.setProperty("formatVersion", "hybrid-1"); p.setProperty("chainId", "2800"); p.setProperty("count", String.valueOf(count)); for (int i = 0; i < count; i++) { p.setProperty(i + ".addr", "0x" + String.format("%040x", 0xB0 + i)); - // fiecare index primeste macar o cheie; acoperirea per schema e controlata mai jos + // every index gets at least one key; per-scheme coverage is controlled below if (i < falconi) { p.setProperty(i + ".key." + FALCON, Bytes.wrap(SealSchemes.FALCON_512.generate(random).publicRegistryForm()).toHexString()); } if (i < slhuri) { p.setProperty(i + ".key." + SLHDSA, Bytes.wrap(SealSchemes.SLH_DSA_128S.generate(random).publicRegistryForm()).toHexString()); } if (i >= falconi && i >= slhuri) { p.setProperty(i + ".key." + FALCON, Bytes.wrap(SealSchemes.FALCON_512.generate(random).publicRegistryForm()).toHexString()); } } return HybridSignerRegistry.fromProperties(p, "test"); } @Test void armabilityGateWalksTheWholeScheduleNotJustTheFirstStep() { // the registry: 3 validators with Falcon, only 1 with SLH-DSA final HybridSignerRegistry reg = registruCuAcoperire(3, 1); - // treapta PERICULOASA e ULTIMA: hibridul cere SLH-DSA cu acoperire 1 < K=3 + // the DANGEROUS step is the LAST: the hybrid requires SLH-DSA with coverage 1 < K=3 final PqSchemeSchedule orar = PqSchemeSchedule.parse("100:falcon-512,999999:falcon-512+slh-dsa-128s"); final var refusal = orar.firstUnsatisfied(reg, 3); assertThat(refusal).isPresent(); assertThat(refusal.get()).contains("999999").contains(SLHDSA).contains("covers only 1"); } @Test void armabilityPassesWhenEverySchemeHasCoverage() { final HybridSignerRegistry reg = registruCuAcoperire(3, 3); final PqSchemeSchedule orar = PqSchemeSchedule.parse("100:falcon-512,200:falcon-512+slh-dsa-128s"); assertThat(orar.firstUnsatisfied(reg, 3)).isEmpty(); // and the same gate's negative control: an impossible threshold must refuse assertThat(orar.firstUnsatisfied(reg, 4)).isPresent(); } + // ------------------------------------------------- intervale per schema (AIP-22 treapta 2) + + private static final String SLHDSA_CANONIC = "slh-dsa-sha2-128s"; + + @Test + void perSchemeIntervalsParseAndOnlyBiteOnTheirOwnGrid() { + // Falcon every 32nd block, SLH-DSA only every 128th, grids counted from H0=64 + final PqSchemeSchedule orar = + PqSchemeSchedule.parse("1000:" + FALCON + "/32+" + SLHDSA_CANONIC + "/128"); + assertThat(orar.hasIntervals()).isTrue(); + // schemesAt ramane setul intreg: cheile si armabilitatea privesc toate schemele + assertThat(orar.schemesAt(1000)).containsExactlyInAnyOrder(FALCON, SLHDSA_CANONIC); + // 64 + 32k but not 64 + 128k: Falcon only + assertThat(orar.schemesRequiredForAnchor(1056, 64)).containsExactly(FALCON); // 1056-64 = 992 = 31*32 + assertThat(orar.schemesRequiredForAnchor(1120, 64)).containsExactly(FALCON); // 1056 = 33*32, not a multiple of 128 + // 64 + 128k: amandoua + assertThat(orar.schemesRequiredForAnchor(1216, 64)).containsExactlyInAnyOrder(FALCON, SLHDSA_CANONIC); // 1152 = 9*128 + // sub prima treapta (parintele 999 e sub 1000): nimic + assertThat(orar.schemesRequiredForAnchor(1000, 64)).isEmpty(); + // at the boundary: the step is read at the PARENT (1000), the anchor is 1001... which is not on the 32 grid from 64 + assertThat(orar.schemesRequiredForAnchor(1001, 64)).isEmpty(); + } + + @Test + void withoutIntervalsEveryNamedSchemeIsRequiredAtEveryAnchorExactlyAsBefore() { + final PqSchemeSchedule orar = PqSchemeSchedule.parse("100:" + FALCON + "+" + SLHDSA); + assertThat(orar.hasIntervals()).isFalse(); + for (final long h : new long[] {101, 133, 200, 999_999}) { + assertThat(orar.schemesRequiredForAnchor(h, 64)).isEqualTo(orar.schemesAt(h - 1)); + } + // and a scheme WITHOUT an interval next to one WITH: the first at every anchor, the second on its grid + final PqSchemeSchedule mixt = PqSchemeSchedule.parse("100:" + FALCON + "+" + SLHDSA_CANONIC + "/128"); + assertThat(mixt.schemesRequiredForAnchor(96 + 32, 96)).containsExactly(FALCON); + assertThat(mixt.schemesRequiredForAnchor(96 + 128, 96)).containsExactlyInAnyOrder(FALCON, SLHDSA_CANONIC); + } + + @Test + void intervalsThatCannotLandOnAnchorsRefuseTheWholeSchedule() { + assertThatThrownBy(() -> PqSchemeSchedule.parse("100:" + FALCON + "/0")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("positive multiple"); + assertThatThrownBy(() -> PqSchemeSchedule.parse("100:" + FALCON + "/48")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("48"); + assertThatThrownBy(() -> PqSchemeSchedule.parse("100:" + FALCON + "/abc")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not a number"); + assertThatThrownBy(() -> PqSchemeSchedule.parse("100:" + FALCON + "/32+" + FALCON + "/64")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("repeats"); + } + @Test void beforeTheFirstStepMeansLegacyNotSomeDefaultScheme() { final PqSchemeSchedule orar = PqSchemeSchedule.parse("500:falcon-512"); assertThat(orar.schemesAt(0)).isEmpty(); assertThat(orar.schemesAt(499)).isEmpty(); assertThat(orar.steps()).hasSize(1); assertThat(orar.steps().get(0).schemeIds()).isEqualTo(Set.of(FALCON)); } } diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SlhDsaParallelSigningTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SlhDsaParallelSigningTest.java new file mode 100755 index 0000000000..d4f0726a0f --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/slhdsa/SlhDsaParallelSigningTest.java @@ -0,0 +1,137 @@ +/* + * 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.slhdsa; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; + +import java.nio.charset.StandardCharsets; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.hyperledger.besu.crypto.SecureRandomProvider; +import org.junit.jupiter.api.Test; + +/** + * D-337 (2026-09-26): the parallel SLH-DSA signer must be the SAME signature as the Bouncy Castle + * original, bit for bit, and must actually be the path taken. Otherwise the anchor certificate would + * carry a different SLH-DSA seal than the second client and the public verifier expect, or the + * speed-up would be claimed while the sequential code still ran. Pinned: deterministic signing over + * several keys and message lengths equals the original; every signature ran exactly 1 + 2D tasks on + * the pool; the kill switch falls back to the original code with the same bytes and no task; and a + * randomised signature from the parallel path verifies under the original. + */ +class SlhDsaParallelSigningTest { + + private static final org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters ORIGINAL = + org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters.sha2_128s; + + private static AsymmetricCipherKeyPair keyPair() { + final SLHDSAKeyPairGenerator gen = new SLHDSAKeyPairGenerator(); + gen.init( + new SLHDSAKeyGenerationParameters( + SecureRandomProvider.createSecureRandom(), SLHDSAParameters.sha2_128s)); + return gen.generateKeyPair(); + } + + private static org.bouncycastle.pqc.crypto.slhdsa.SLHDSASigner original( + final AsymmetricCipherKeyPair kp, final boolean forSigning) { + final org.bouncycastle.pqc.crypto.slhdsa.SLHDSASigner s = + new org.bouncycastle.pqc.crypto.slhdsa.SLHDSASigner(); + if (forSigning) { + s.init( + true, + new org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPrivateKeyParameters( + ORIGINAL, ((SLHDSAPrivateKeyParameters) kp.getPrivate()).getEncoded())); + } else { + s.init( + false, + new org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPublicKeyParameters( + ORIGINAL, ((SLHDSAPublicKeyParameters) kp.getPublic()).getEncoded())); + } + return s; + } + + private static byte[] ours(final AsymmetricCipherKeyPair kp, final byte[] msg) { + final SLHDSASigner s = new SLHDSASigner(); + s.init(true, kp.getPrivate()); + return s.generateSignature(msg); + } + + @Test + void parallelSignaturesAreBitIdenticalToTheOriginalAndTakeTheParallelPath() throws Exception { + assumeThat(ParallelSigning.enabled()).as("needs more than one core").isTrue(); + final int perSignature = ParallelSigning.tasksPerSignature(SLHDSAParameters.sha2_128s); + assertThat(perSignature).isEqualTo(15); // FORS + two halves of each of the 7 hypertree layers + final byte[][] messages = { + new byte[0], + "a".getBytes(StandardCharsets.UTF_8), + new byte[32], + "anchor parent 20333167 on chain 2800, a longer message".getBytes(StandardCharsets.UTF_8) + }; + int signatures = 0; + for (int key = 0; key < 3; key++) { + final AsymmetricCipherKeyPair kp = keyPair(); + for (final byte[] msg : messages) { + final int before = ParallelSigning.tasksRun(); + final byte[] fast = ours(kp, msg); + final int ran = ParallelSigning.tasksRun() - before; + assertThat(ran).as("tasks run on the pool for one signature").isEqualTo(perSignature); + assertThat(fast).hasSize(7856); + assertThat(fast).isEqualTo(original(kp, true).generateSignature(msg)); + assertThat(original(kp, false).verifySignature(msg, fast)).isTrue(); + signatures++; + } + } + assertThat(signatures).isEqualTo(12); + } + + @Test + void theKillSwitchRunsTheOriginalCodeWithTheSameBytes() throws Exception { + final AsymmetricCipherKeyPair kp = keyPair(); + final byte[] msg = "kill switch".getBytes(StandardCharsets.UTF_8); + final String before = System.getProperty(ParallelSigning.SWITCH_PROPERTY); + System.setProperty(ParallelSigning.SWITCH_PROPERTY, "false"); + try { + assertThat(ParallelSigning.enabled()).isFalse(); + final int tasksBefore = ParallelSigning.tasksRun(); + final byte[] sig = ours(kp, msg); + assertThat(ParallelSigning.tasksRun()).as("no task on the pool").isEqualTo(tasksBefore); + assertThat(sig).isEqualTo(original(kp, true).generateSignature(msg)); + } finally { + if (before == null) { + System.clearProperty(ParallelSigning.SWITCH_PROPERTY); + } else { + System.setProperty(ParallelSigning.SWITCH_PROPERTY, before); + } + } + } + + @Test + void aRandomisedParallelSignatureVerifiesUnderTheOriginalAndATamperedOneDoesNot() + throws Exception { + assumeThat(ParallelSigning.enabled()).as("needs more than one core").isTrue(); + final AsymmetricCipherKeyPair kp = keyPair(); + final byte[] msg = "randomised".getBytes(StandardCharsets.UTF_8); + final SLHDSASigner s = new SLHDSASigner(); + s.init(true, new ParametersWithRandom(kp.getPrivate(), SecureRandomProvider.createSecureRandom())); + final int before = ParallelSigning.tasksRun(); + final byte[] sig = s.generateSignature(msg); + assertThat(ParallelSigning.tasksRun() - before).isEqualTo(15); + assertThat(original(kp, false).verifySignature(msg, sig)).isTrue(); + final byte[] bad = sig.clone(); + bad[4000] ^= 0x01; + assertThat(original(kp, false).verifySignature(msg, bad)).isFalse(); + } +} diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyIdentityTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyIdentityTest.java new file mode 100644 index 0000000000..44a80d34f2 --- /dev/null +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/validator/blockbased/VoteTallyIdentityTest.java @@ -0,0 +1,75 @@ +/* + * AERE AIP-22 stage 3: the ONE place the validator set changes form. After block H_id - 1 the vote + * tally holds the sorted post-quantum ids of the same members and open votes on addresses are gone; + * at every other block (and disarmed) the tally is untouched. + */ +package org.hyperledger.besu.consensus.common.validator.blockbased; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.PqRegistryV3; +import org.hyperledger.besu.consensus.common.validator.ValidatorVote; +import org.hyperledger.besu.consensus.common.validator.VoteType; +import org.hyperledger.besu.datatypes.Address; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +public class VoteTallyIdentityTest { + + private static final long H_ID = 1500; + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + @Test + public void theTallyTurnsIntoIdsAfterHidMinusOneAndDropsOpenVotes() throws Exception { + final JsonNode v; + try (InputStream in = getClass().getResourceAsStream("/aere/registry-v3/ok-epoch-a.json")) { + v = new ObjectMapper().readTree(in); + } + final PqRegistryV3.Registry epoch = + PqRegistryV3.load(v.get("manifest"), v.get("chainId").asLong(), + List.of("falcon-512", "slh-dsa-sha2-128s"), null, true); + final List
    addr = new ArrayList<>(); + final List
    id = new ArrayList<>(); + for (final PqRegistryV3.Row r : epoch.rows()) { + addr.add(Address.wrap(Bytes.wrap(r.ecdsaAddress()))); + id.add(Address.wrap(Bytes.wrap(r.id()))); + } + final TreeMap epochs = new TreeMap<>(); + epochs.put(epoch.bindHeight(), epoch); + + // disarmed: nothing happens at any block + final VoteTally before = new VoteTally(addr); + before.translateToIds(H_ID - 1); + assertThat(before.getValidators()).containsExactlyElementsOf(new TreeSet<>(addr)); + + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs)); + final VoteTally t = new VoteTally(addr); + final Address newcomer = Address.fromHexString("0x00000000000000000000000000000000000000cc"); + t.addVote(new ValidatorVote(VoteType.ADD, addr.get(0), newcomer)); + t.translateToIds(H_ID - 2); + assertThat(t.getValidators()).containsExactlyElementsOf(new TreeSet<>(addr)); + t.translateToIds(H_ID - 1); + assertThat(t.getValidators()).containsExactlyElementsOf(new TreeSet<>(id)); + // the open vote on an address is gone: two more votes for the newcomer would have reached the + // majority of three with it; after the switch they do not + t.addVote(new ValidatorVote(VoteType.ADD, id.get(1), newcomer)); + assertThat(t.getValidators()).doesNotContain(newcomer); + t.translateToIds(H_ID); + assertThat(t.getValidators()).hasSize(3); + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n01-format.json b/consensus/common/src/test/resources/aere/registry-v3/n01-format.json new file mode 100644 index 0000000000..24631f15c0 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n01-format.json @@ -0,0 +1,62 @@ +{ + "description" : "formatVersion 2", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-01", + "manifest" : { + "formatVersion" : 2, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n02-chain.json b/consensus/common/src/test/resources/aere/registry-v3/n02-chain.json new file mode 100644 index 0000000000..9bca0b7f72 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n02-chain.json @@ -0,0 +1,62 @@ +{ + "description" : "chainId of another chain", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-02", + "manifest" : { + "formatVersion" : 3, + "chainId" : 2800, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n03-extra-field.json b/consensus/common/src/test/resources/aere/registry-v3/n03-extra-field.json new file mode 100644 index 0000000000..ef96f09515 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n03-extra-field.json @@ -0,0 +1,63 @@ +{ + "description" : "a row \"3\" beyond count 3", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-03", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + }, + "3" : { } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n05-unknown-scheme.json b/consensus/common/src/test/resources/aere/registry-v3/n05-unknown-scheme.json new file mode 100644 index 0000000000..1ebe53acad --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n05-unknown-scheme.json @@ -0,0 +1,64 @@ +{ + "description" : "a key of a scheme this build does not know", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-05", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7", + "ml-dsa-65" : "0x00" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad", + "ml-dsa-65" : "0x00" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n06-required-scheme.json b/consensus/common/src/test/resources/aere/registry-v3/n06-required-scheme.json new file mode 100644 index 0000000000..af77e69fab --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n06-required-scheme.json @@ -0,0 +1,60 @@ +{ + "description" : "row 2 carries no slh-dsa key", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-06", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846" + }, + "pop" : { + "falcon-512" : "0x39883a85bf19c4c6226ed184055922f5d874d232c6c5b4df688d7bc238a8f67c4e03e4747c85bda6563341e70b135e469907a1d3e727489e05f7ec8a668ebb8583fa6a118448a6668cc5bfea74d54f5b38a2e7f8714acc751678730c0d3fe735e8c5b7f02abf9315f778e0c8cec1bbb6aabb918dcc9c99febba4e3c3e3ccd5069799964ea34437aacfd7a44946125d107ee1e11643dd4df6be35899e64b6bbaef2b1953e5bbe21abc3411a494157f6797070f9122eed64924218259599ae69c2d7941e67729bcca3ca32eb2e0915f3a786ce88b25d30019f134bfa81b6475cfe74d9422ebd3481ae35e4d052ae0d68a77a14d67d9e409bcc6eca46b6c71b9b946ba06e70b64a3b9eca1ea338a7e521246d017e2107aab0d5e1a693ac713edb76280dc3076375a0cdcb586f712500dcf9f0b6b2846714fd2e2511fc7c5f0dea79fe5ba22962cf01b4203deca1fe5b5259c3db98604ca7424db15708ea0d0cebcbe0f828a3a9863931785231fff9788d3b0e6c52220927583539240efebcd9781eebb634b72549d24be2ab6cad7da8366ce61e0665314dbc06a907a63f3d744533659364adb9b5cb6cb59903a66ce97b03e88f43ead94607e88532ee2b96af40bd2b8360bddf18dcd9fe732608541e74ea6a3e14121f468a92649acb9650efec47b3e4d1a7d784a8b0c7323cdfe7f2390ad61f49c67e2093c707b8cba1af39294fb6c86fdff3b645e25df27adb7970e2825e0a4f61bd35f9fbb53b33d7cd7b9a699c7ee11959f11b2972a91e2f92129a3055d846945731c6c705b4fe7ed6372f952b57fa7ef5b214cf6125059f23845655caf4ef1ee351d4baa9eaa5a72de4cb36847e787eda3ed6338bd7cf92025db415df395f22363db33974cd203b583339bee8b99dbabe3fbc8e6ecb76f333278d08" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x4732138455fd4f975bdd2fa3b9b2d5717315b78648f8325cca9d350bc460a20c14ed099ecb4e29c7d37c715e76c3df0fc30462e41b0055c29b4b9285e46c434d00" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n07-falcon-header.json b/consensus/common/src/test/resources/aere/registry-v3/n07-falcon-header.json new file mode 100644 index 0000000000..3bb2585b28 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n07-falcon-header.json @@ -0,0 +1,62 @@ +{ + "description" : "row 0 falcon key with header 0x0a instead of 0x09", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-07", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x0a501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n08-pop.json b/consensus/common/src/test/resources/aere/registry-v3/n08-pop.json new file mode 100644 index 0000000000..128c8d19e8 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n08-pop.json @@ -0,0 +1,62 @@ +{ + "description" : "row 1 carries row 0's falcon possession proof", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-08", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n09-id-derivation.json b/consensus/common/src/test/resources/aere/registry-v3/n09-id-derivation.json new file mode 100644 index 0000000000..b1c2c8ddf6 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n09-id-derivation.json @@ -0,0 +1,62 @@ +{ + "description" : "row 1 id is not keccak(0x50||0x20||falcon)[12:]", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-09", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x86f21da7cc9a47f4030f45bc704fad2154b45bb9", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x39f3424cc32c4c2bdb8e81d8e158b4b51a163f5c389e440e053666da13d90b2c55ce9034afb919a85ce9dbfea3f4ad217f67759764f5f6f59e6b8bee9bef8cd431f341653d4678ccacc60919ac21672f44c5bc161f6f9d9bd056f41be829666814d847c3f931db1b78a3c8ee17929542e9ccfbf40f5e78c1a29804d337a672adbc0ea63e0367acc6b2e702b295bc31c6a52547afce023b4177750fd5f929dc99076dc0ca2e9258c5c2bca3c3893ca9a00d963e3aecc3dbd9bdad2120a95a670068daa7504392cda930266cc41ac29a1d8805c0d0cfc75606f4f9b5587d6d1b97b626ec5bb7e8dd2f48f2706f64e76ea08845f45055a6a0b5eb5f7eadd19ac36ae12a12cf2ef58e2c8f43359845b51fdfc523ccebfad1b4ada4d14f4de5da0313cd309872608b44b190cdcd45f09f4ad5940d85d8083e3319c19bfe227d6d8df56152aaf11cbbed2330afc689f3a4dcaeb4a57f0537edd510cde7704d343dc54dc65db05316b95ca44aeeaf06ef5ca2abce8fed3095b35cff2b6afbf5e47e672671aa73ec92b9d37301d8488bb2ac71b28dd64eed637a56acf24c6f9d34bfe58d9645e18b0cc939a748f04a396746f3158601d95fbbfd7c8904932a0eef057843d44f6a01ec297b9dbd5712ca4ff83022270279ad1164a623876a75983c3ef7a7de6449dcaf697bd023d728ddab54fee05b5d4d8f3755a74a3507ba96c2e3617f78645a33a8a4c5f0869c9e212c09394e5c03d3bf631bcbe4394d4da8dddd7369df38b9ec83cedec3f03475914390361cb5522e9d1df381be267af77bb7e1b8ea9ac4679dd4c360153f96688c35a8d4fe12db57b2fd895c5327b98a1024898f50708501c58a4551c55db5d86db3ecc4ad28dde6ee8d0eaa0b7350a7dd4c4b2cb3252b5b3499a5b44b9a333c80", + "slh-dsa-sha2-128s" : "0xc7c85a7c49c8c259c3166dce0f1f2155457e792c84764ce212d277586195859b6144ea3b90735555fd7e96eda0a2b052e80e4f20968de14d33ab27bbd1d1b83ded17b9f69ca31ecb4a32e6449ced6433ffcb6b8bb933c59c09fca37a16fdad554067ae6315da891ae10e8e706f711a3a938c6c1a06d6d81e0fc5351e0b2f534821ff1161201697558e561743ee8c356b0ebd165573a162457de3acc60a2f3c299e59b04f0a28b944afe344ab8981486b4f84327980cc2634b990ac26fbfe2a2def151f21c9031797d2a683faa342035e35cbd6e63621ba78291d6cbc5d490eaec4b683c831a2a3b9e3b7f56f6700ce9e8f536177b9deec6dbaaea4709454cf791c7175149190fd61ee615e59b896272f7f03008a4ba435d3690806c9798827a5c02f4e9d6cce8de388465a98794b4180e7c4224fda72659aab19aacbce0e0c5cd0a4b86169b15939418adabffd03c9d269cbd16a1ae4350eada6edaebd686c70a8e50059529c19e1b7c4a3e9a1d8dc336c12a948b48525fc58e8aa831b775c9049ef47c9665468f67af5c6a877f7831b9367015059ee1f39f42494f6718cf5fb7f53e6c85e59a7a1c14c6b63bae82bd446d39681e556d7f4ca876b3a1115eb1c901f243b6dfd47f5ea7e956978aac47e6cc0e4aba4d26de8ad159274974f70765ab04b88837322f22f3f8ccb9fc8616fbe6dec1fea6aabd395a9c0efd22d43f57f920daf9b1e7d3cc7822c7d696be96ade2979caba15bef0da98531e0cb92924910c5ff57f918431d15cb8b84c3e862c940675f0992d447aee93188848e61215b8d70e787c0f2166c367592f305d5f625ce2f824927b9c626a365227095e1beffe2da3f00c651e821f9182e736f3e01fb05aa994705f19355c87ae7bf02e1905680960408899e7a05098c9fe82100dddd51602e6b1e341dccfc4c29190a798192972d7101ab6c86141f88ee3ff59481331072e4b0a0085000a7d93f3f46e3308a00a8bdce9dc9952a0cfabb98b564580d5ba1fb14f02592b17d5be74aba69b30871f8949764e01019c760f1ea2a979104243cef01e64f7f26ade55770c1226db30e6b7f0ca5e9d4bec189a2b88e20518b579c94b51c0e45749c7aa64feebba7acefdbe4e2c305736a6118f237b5f84fdb2adc315d3b6c33ccc5d56b4b25cac9953dd6d1f4ca03d97ae0f624565251b3b97e5073e988aadd4b1803bccea1a0f07423d00f805645c5b5c7227939d55ac9d9aaf1813877ef1ef420703d63521ca5e2561da45532a131790ae3130b96bcfb5419c8ed886823173c80ff92e7a3efaf6e80682b67f63108395c35d896423bd4d9195f3c186da8de257ff0927bcd1c33a95effe0dd33776af45bbc4be1611cb5868005e6927f0bd9fdd39682b9f3ffd062b5e4e5bbc9354e8e2d9b4db2afb198ec0c5450c43d5eb09c38c7896efe0e129c286445977e3be9496868ed3df248f9c77a3c0b455f37ab3972b28936526e611c7c8052e0ce28be6167b838684fa2a41682a4a15bda1084c8668dfa606287980af5457646682f04aa62a0d836d8417faf52ec1c4434aed5519491bb07d27042a056b782b7bd7d4ba8f4afd9e54f63188b49edaabe72fb7acbf767f83416d3ba90efa9995fcb1d7c89a01d1ca9e2b9d9bf77f809fc5fcb5ff916991e94040f242d2e1d35f3822c46c720d8a4b283409e55dc1ceff3d7dd3e64329e75b295ea492ca819332f0c21a07097b2d14200b2055bc923af6b4d6c6fd1d2b4337966c2fd781308b8beca6fb6f14a81fe689ac6b568f7f86b60b7a811a34b7dfa6d61a1e4caa2b7bbf2c19e0c41165309e38e3b55f3b09bf22d98fd5f91bd3f68b1c08fa83d003e962863b56cb24544db5d25989284c154221419b7de8bf02486b57c00fc51a1cd462275539c9850cc5e170351a9d63a27971d1830e4229d18dad2cab73dafd73950c8d41aa046bc355ef892da8aef82dcc85667453da46868e23f647dcff6299f349972ed8bfed7690c26b8277f8975664e2fd30bba65b90723e52315ee80bceeb6234f42497bb1936ae03dc7de1b1d62b0fba152fc742dee4ada7981698936ba45ed7619c6a1c90a9d7d2de176a54e2fed25ce0e4f204cd6f3a8d94ff34eeebe9af8a97d7e6baea00ab7006ce259e523cdeb32404ed38073b04e23e70e335f0c6ecfae71d80136d8d4b4ed3f213ad71514b0d4e79f4564b894eab00d5f401287d4d2be7763131847a7051d1c3f399ee1def1375f0b32475725343a37c279c4caf06978a53594110dd581f7820361ea9110458414682963092c241b10db7c89b364a71f4c576d9808f47f2eeacd124d081543ab4b9433dbafe8e90a8077c2024e47b1109bda4a61ded707e89cd60cff05e4aa7394cb4bd01f4abc8d7253682814bc3ffe3c3599a80ca2cfd80a0026e2c9341f9bbcb3de905f33a45bb4341ae88b462434516d640802b73272d25aaeb5bd03d6e72109a23369fbe99b2bce745ede6b0dd7b91705e9139ef77ac2bc0ae58518214383a0286b98219392178f79517ea14b986a05182ce744956107207ea9c5c2d793f83522ee9937bb2cf2e2839a06a6fc813ae065acd91dbc6aebdda4d144519e29293fe6c03def2e4fc6c8dd82eab21bb4e752576c6c3e9e10f9561383ab8fb056dddc870ba7e43067a8083b703ac3b7bdd9895c369c7d543dcc2b3d57e5bd1e25fea5581397eea7b5fc87dd73ae2279a13122f1cfffe93875a075fd71e5d2e600d310a898c79b901c21a2f887d76b9c863129f8550bc909e401eac2eadb17ced4f2e42585620a95a80c375dae577fe2271901b225c29da22145be6bc1d15159733d29504f21d4b61493902e5d3ad7e9868841e3d9097063caffd1aab5f6e8dfd95250a42878169e0371a3b9f9efde9e5215e02ffcc8609ed56c01c2d8a2b55a3283e9ce93b9b10930fd6080f40ccf098674cf2291fa1a58a00ee97ed553578a4129f906c1279fcac25ae1c149c7e7a2f0d445a31113584b481dfa6a915f3189dfaf10745a1f76d554830eeb8834ded30dde668809de0d570451f84ad457719c77b50fd6f8d60c03999e0b99cdeff0443527c5613bc89b066c97fdc2cd4a96c1fc84ca558f706fc0567b05d1b691a819870127c8fea456602610272c7d023df1476bfcc60cbc0d44f6e6d2e93c8c14d1bf1c71f1dd2bb3253b6f62a4ba9c84eb1a7d15a09a899a4d563b778511a67af7c2d5d90ebc2bf8a46d1ce14d05281591a8c5c866559535bf6467b1b7caf6d847ba520526083b1d56561650e3be0aaf44251190f0f0d0624a789754dedb7088c4b66dd7c144d873ae8c510e1673fd52c12d0c0253b38e395de07fa36d6ae61d23fc3035ca708ef3f3913061477856a22647b022dedf89896c117c1b85338783f72334951e8451aba87a4111cd7a6f04bcf8ecfb113d2e27e4eb50bb0232003673c7600c5a4242cef139fc14e0b532bc80a64c51b0e8767f6432236c0582abca2b2f1d443f2048684a1f8af6fe360a8dc0ed0760660bfeb4852fc807df06a58482de1affd3d517496020fab2f2b37fd445f5cddec391440d0b890c4453e42b5eeb87f9a5de41b64c5d38356884a8766d6ecdf800bb1636143e0985d01368081127681c06add07c2ce4f6e0633f35e23d49ffcf08f770312649710839b9e17118afe60aaeb1442ceae9f496669e8bd1e84885e39bc1ba934134bed0bef28902b69e851600945b7a96808b5039181e990154a17825570e2b273490c52e1817977a9f3b21700b10f74a80cb15e795377dc0b252ac9889480c7888cfdafc28b247c719fe579ea08cd93fec1d7debfdffe80ac4505ffa1a717468849c6f6bea9d6b835c37a17c768c9db73315a50a09e832bdb2c1de657e75e6dc6f5a7225d676dfa4119dff5c1d3d6a82ff1d345d1e90d43bf4f8afc0103da3c1984261b1922503d86379753e9e5d63ca9c6bf75ceba231276eac4823a1a87790fc242aba218c2fcfec4ccdbe1de393008ef6f44101c41aeebe372663c5687c280a2252adf1f9970587c03cf8130dd906a4551c24fb8957e12e511d38cf0a14e2cddba0887be829eac0755797bc18ce4e7f2150701bdaa3ebc97e201289bb7a0d103cc7adf54652dbf1fb2c3569e80c74c8a3748105fc272cb4c89dbd99c41cef171abb0209cbb1be8f9faac8452ee32d009abacc60b8b75d0acead3ead7385aec2a879983460b3f3cc59400eeaf9c60a41775bf759aceaf899a13f715845e7cec0227369073c94511a184e0c3db3e246981a1d24c7e62befa4df3cd0a0850c6a4e099bb3daa120e053a899e176bc092acfb1780cbe90a995b25b3b1fed31623d7e68378f676a07ee701fa19334fa83fce3fea5f1b3d3c3f5887e74fe6e48153670ae27ad84fa7ed88cc22f6b1ef393bb4cee3fa63c195a6ffe20d6c3f82ec03b81373c6b352c5bd3c66636f57c823c4aedb29d8024414c258c723f3b8e6657d8d08b812cca6af68fdb92bbab15733d1dd31fc58a2016a5c4fa03fb0653f910d6dd1a87cb28533b3508fa5db1e3612d1e2b319daf372e0b775a6a0328859a2d4e6d89ce5b3263c68009c3cedab1a79e61a66ced75495834b87c444268c86c309f61f41a2484394891a46a7e16fcd5622581b83569f281ccf599d5f045671e666a6036bf2b65cc793f3794fd829052303a64b0363177672228c359dac45163058c5d2742d2dd86771794777548754ee49f3d4651f292ea30ec7cf3e8c5b74db15923991015d2f18de70f79a73169c6a4fc5819e9fdca7b3d7a4720d0d1de141c91d6e946c25556cf4d272b9c2ca25ef7d4f028ac2cc1bdc4fa1d579f87ce31d514c2ae5b3072c483d274d984eda9f6450a4f4bc47b05fa75d9f6a3152027315aeca879d2f6a27a342cc9a2a2109fa73fbea16aea9c25294c9276d84fed450cfed38f34a3daf82d11c22e9554140f32cc16e3fe737fb403c3b9e44f6f3ad417e6c3cceb76e104f184382955a5e87860c455458df5d8f8aa44af9571dc47fb337c83db67a6eed4a9a41b912a6d99ef15ef9672ebd91bb62037e46cf91f68fade2eeeb5fcfdf4b86f6d816df1489bad34bbdf99ddcf7f18d1062e05d8f14cf41f7aaabe22768a123703a96d561e01a693df984a7c719d603a32d8f45de4441e04e208ccb15adc9813ded7e9197a9adc6da6f16147da196aeaf9404a8e6c8395a2705da81c9f3e75aa9b0798e35250299c250e4e4540b4e3ffa1191292eb44160779bfdf10feaaabd081fe975486c458e5abf97aa45e26cce18fc749aa200460a54cc2293647aadaabe70fb01c7b3670700a419025949ef9f7677594b85d0837977640156acf48678afa477a242145a1c8fcea198d1b6a0f296568dff2b8dbb8b79d5151c68f9b7a9b8c5f9ba9275dad04de3dd7563c71ee0d62e3c068745ffa79b42085862c30eafeb806408786e491ed2dc80afe93879d50117063aa16024ff79aa6cb15d19d5affe5b0c5d19d53c48e1185f50e1c49c34d872895ada2bc63e71be3f578384171a4faefc33062e1c5b8afbebef3931f33864e6a6f180e2fbf24cf70bf1f9114ab6c3e360a9766243ae7e24a5a1e9ea130745a4f17007c16ed3d0b2a3aad226a0312c968c0ee2aef7203af3cb5f8388e1c723b8d3f1118672daf87d1e8652c8b2aa59fbdd56cc98200f3eac2ab2d95961e1cbc3b7a7d1f89786d73be0823aa7df818482def88c7cc1041d53f11cd4c6223c7988aed51002f20eacd2e9655fce66a5daa5349500c9d083680a4f282c337bbcd957d47e2f7451c41018d156720f135ddc641c8569dc8a9cf77528e1c9a74bd2796cdadd67b42901ac0951377e5b2210d2f48b2d4322fe200d4834713b17ea5e38bd6f518b401c53a6d0d60d3691dc895843a7419c6960301e49c6b263f9b31c797ba2294a3a7b3968a13bf0bdb085b357244ffaa8c20da84be65b9d1a3ad287662d8b99a14899fcc21f423f739440bb0bfd51e3ac3196a69a3b145eaaa0e5d1a205e6168a5bfa92181e7e7bc8b6fcafb1edc5837f619736bbb8d74976b07161a0206dbc0a28baf0dfd259485c3a02bb5c0e5191514fdd8bf09d1006940bdde987454af348b9b9e5ac70a981f9f560c5e213073c6fefb674d482b037657d79cc284f33fa37f7e3e854da6f4f2acceb8b946e7af85770d8cf797c88d8c710ba552139749a256b5d34d4d2d7dfde90c4db2ea791b6a02b9882a18da502ebb22f2ca33de1bbab158cff5604586915f018d852338ecf4fc6037cb9a54f237cefcec458fee8e35693d7d369bfd6c8b6a328af535d7e174173fb9c0205b56cfb97330b19a9ef51dcdd5bdc1d7f4d3525334ac963c9af3253354bfef26e5e1a76244bb8bec800eeecec7698eb024591c84aa1c0fa83b1936773998e37c241171511bf9f4a419be8e65c3f619d25e04384de8f7977ab98feb8ca915f724fab36ff48e36cde7df3a27528584e6f89de3ecc28f19290113781d41d1d18f6970a1ff5b24490daccf30012accf94de3620c2e90b3bc107fc13196e002b4c7beeb0ef0349af18aeac3a40ddd7c6c83d0729f7ab8109d18f2d999fa0a6e34268c96515046c9d2cb7732ad95282837f826ad3c2ac8e3ea1e4dd7391fcc76baf9c3ee7402448d5921f9654cd000466c302827a274c041a58eae83b0c89f1b05b0e72710b4cfbe78c20c35f87096d54cd53dd16057a48bcdaa0c3d29d4bfd8c1d275563862ad46fdfb9482c16b42a084b4e566539e31a3d71265e2aad2875f526b6422e69d4770bb594006069814a12c7a91f4ce4eb96bf607f8034c1dcddfe63e7c3d24c35f4aea46e1d9fbeacd471a21837efe0d72f9463f95eae3720316b3c1fe4de540b1b50b3a462e5bf9a2a948fcd93a5cc343c22d535d46a28aa78eaa5a9899acac5165e0ffe5ca431f535e1445dcf55cc9836323a324d7e9d9131564753f1a1717fabc4f37631b99a48b409e7d55b09793c145431cc0d22267cef81cee5bb984ebc771cdcd7fa87514734854ef98997f9e8e4fdc6799f399e8c5e2300f70998166217e8a5e3e981f726d373a317821c82509f4fc043d967edc5cf236b156f7ce452abfaf08702e2020f747f23d0cea8abfad05a6745bd6c96f93f073cf10bd23f15fd35c60c65972efa01e2d506e32276813523045fe404567905e25f4d185069402d43efe34d52d3027bd03ac9be5e0e253dc0bede7f6a3cb758b1d0e272078ea6d74d255f69ac64a92e8a10ab63b56aeffc12d77e8f482fa2befff52b78e2f2d2b2d7647eecac8daf8adbbcbae213141723c0b82506568c8ace99a6d1881dad8cfcc1ba5d802f739dcab913d563fc27e3980ec2f73ab67516ad50fcaf25fd273fcc860742cfd6df7c382af54a49b88a93919a1764d0d5784bf14d9f4e70556d2681d66f3a20d4374aeae1e3a8f39b430ee096e6da798714cc7aad2635b387f9ec61808d45f14fe5d386bd34836e40cdf7ff34200e4564043274076ad36e31acf26d3f44b3456ac7d57339dce14323c00bf8974c73f7b509c62b3abb8aa331d0a673995aad271194e5a9fb0f142226a6d4effb0e7f6563541c7a3bcc95267c255fa118c24b2b054e5bcfdb95fa8ab86bfe9d3317d405ab3cd37b79b2256f9c4b94807a0e141007a001936b510a5372bd5bca125e924bbfad8e3336f10253196ac8b4a7e7fdd6cc3e483cd862e0c6c6d594aed8462ac51b83de7dd5a4e840423be98df6b1a114accb3fd74dbf904ed900e0fd216704e6ed754c0d720a220f32c3422c341cf5eae6589645c65be7704db6c7a43c558506129155637c41f2007e1c2c41e3c132131364e863b9824861c81306f6d20fca430c1029d57c00826b66aa7eac954bfe189733220c00af05fed6093cee3e907b9cd2c2af7ddb3aaa818b6b168a16ce4bfcea932749f061c222fe6eb611e7efc04e9eac547ab14e6dfaad3f7c06ef3aedee6022434566e1921206a45b5dd0650dc91ce611203ccf8dd681ad9eb6907a7a19e52c61b4b2808e64e4ed89933d3dde4d7231730d8b840ab49b3f66977f2d1002a753b42db089e56b2823359becd78b1034a25d99198fc3902acf7393ceb0003327f6c18bd034c18cb6e63a1dfcf34ac2fe2d10af83c7330bf1d9398e0ddd692a7825f9c5a4de0805039d8b4176d16c7ce11a067595b86f019efa07d13ec489981b3f486f692ad112ad80779e0e8b341817e4631fb24f7696800a8e9c42eba1a40bccc8cf48d6d8a2d4f059d2a63bacb2f338232c90e5eb52fd4f8b303ca88b7aadc04174d76f6441d603cf44d3edc643c1d38c998b58902c01cdf97ddc86f029ace8adf0404c3dd922d64eba76248b894513952c9590421083b10f9b98a1ea57992b547b41360caeee4deee0fddf0d86ad6d6259000b22c92826444b9537f62b93234133b82800ca517a91dbc6e16473e798fdbfe780495225ee2930a59dc942a8e35da7c3ad410bf053619463d247349cc2037e8fa579c2f93bf857253d4da9e2f4023ca2b14ba9f0aad93d1a7609bda79d9877ac81493b68a571276873976563d1251b0c22c684ae9223dbcecedc3d94de456121428a82912364433951c8eeafbc4574f5a00b81428194cda9707e3744ff79699809037b460dc604d2153e06170764cd5be276c535a6b8942feab4dcebf49f41d533e58b59e1de21f0e528c6f868fd3a220b36e24cbdf1af8a89608da5de874ea3b57d14424712edd570325844a1624a1853ee4ad74aff09ab375d4ae7b8e431c1d5d63d3e7ee51c4d22e73652eea838329fb4bebe3ad92ce7152bc3a7660432483502ff6e392ca0d4772292a988aa002946e4e3a061c9a121ffa27e8032d60f58cdf4e79dace6bd36c10a09b4a298d3b8a76bc414bbf56e88b171a1774e04cfcd6a866ed8cb1ac4a263b66458f530de14171e9cbf85105b892bf7b450938483082d8989aa3582a3909bca5c03639d6045d072087ec38b0bc7b9fa024be7144383d669e2e35915d0fdc01d581346bb0e5d8b47760738f772e08df0bee8b5c8b575c2b4b0b3de1d1f5fd260b54c0959d2fc5c7241444ffe0bdea0fe520d60a7e54b51686f40cf6773e3e4287bb88fc5f0f869632764196c5ffe3b5a3c66cb15d44d945b992a4bb84f617bc6ac90ad040765a59ec312bd0b95ab823a5874c4ba4f9865c883a9cc493d7b4e5e09992dcb998be63007eb1d675cd294da94a8a1504e6668b45dbfd3aef4fb08e9c737f7bc1434afd489f490313ac9c719dbfce1e88d8ab5c99ee542c9ae97ce13023bd041875503cd7c9e6d8784f64bdbdef2da7d682617fef77416ffef3eda866c38c8e91570398ee3168b7f834d2c960f54aaf2f80f6021b7eaf8182303b4ed9bd4eaf5049750e29203a45b7518878e3e0a9d2674b53881a722a246317c4fb472231af1dfa2a945574c60c507db33f6edc54a19219e5d5f60dc2c6fd81726b6735542afaa228988f5bb4a22ecac6279212eb4790f551451b1bc68a43a7f29a08766946bafa5b678782e027ed4d2255f4c93d753fddfea84b1e41f5c444dae8dfd3664a8d708a20ef2b549aeebb4423b80ba61ef617ce10ab702d4799f3c40fd37490f5677ee8784f6dc1dba9e731ec6e4768db926748f962be373e4ceca3f399148ef249b28699e9f4f584d4b66ce4e64ff6f50938a80a2cff992321577b2fa29455cbcbf97bf4effc227c79b023a0b836da9be9425b68d258c800b44e3acd4c8de445f1fadb1f5c7f005c44e6466199f2d67ddd0c660dbe014d8562fe6892dec8d2ca3d16413845c424a015fb3d3f47abb236db04c429fc988c2c220bffe2e442fe50c7be1114dec77b7a7d172346aa9982c93a33d321668027fe11f9dfb5deba429920966f92619d443f722066ace9e3304f9b8340b069e8de27363bd12202836ad9ef1dd99d94918e92a75dbeedd4b7a6c307e6a9c0489788976f330806a7fad424076d2b3c79e3319a2001871a204abda1210af00d3380c863063d576377a562bcaf0978929b3e0c805aa4fdc51365f5ca95ef1a0cef3229c642b6b9501b5765159ef34efbaeedbdfa7a3a9f075b411c8ebc0b82de72515ad02b39a30c23aa68be4530137ba8bff7e341c788092887cc9c1def3a9ef754ff324501c5cd3cbfdd635dca123bcac7093a767e25caab6cee7bf10fefce0a2084e2e040f8aa489698282c906bbde1869e7d605dd5d9bf8691863d007a6c97adcc727179b5ac7c4c23ffb9b3b70e6108cf95c51f2d37e8f65360b40216bbd05467d7edeb9f63a9fc45daeef5f2fbed0a0a7b0dd8b84d8a3d07041b576e20cdd3780fddbc1a3ef9cc8ab5f5c50227e8dd2303a9dd1d24b7b7d96de315100a7762c48f5aab88ee853eab14d6444be1605b91c5a4c50c3a4d6e921a7a51acf631194d60c5a73249a899880f57874d3986468c64e894c73c13df92047a48bc5720cc6be4fb06738679a82a4b6e5d41cdc5dbc3661073281076d73c0b86177b6a0a61160e92dd68efc9900ca45e59c9139a62c08a603aa7cf431caa68ab3acf69efd51619684ec79d07727cef9ecb8e1a093dc2d7e110234f413ff77cae5260135de14d5609c86339dc58e0f1b26043650dea0bd6c7f1501a3e1f224c940eefba7bc42ab4e8ff8c35e6889d141826eabd2d3d6b86e8ac54d462c26a2b04543a63448939a632978940b1c025892d844b0734499e55079fab745a152dc2afc87c7dd175c12c113c0596d7889458101326e1666e63e79b2c0bcd0b5fee7285bdda22d6afe424fc344773ceb5339d046ae2b0dab8116286bd8475af7f22c02099b839c48b8b138dea653f3c57bc0ad6684bcd75163eeb9796ccf3f4fd9414b3cd778b8640a4d4d459350e6908708466cca5bacad3f3c06cb6be8528e27d0a9652019cc1cb947a1e486cb612e320e4d309d60aa6eb6e6521cf8ba9ea67333c8803b9c6561d87e1cbb377b2a0d97bf6469957c3e4d06e64b2420050ecd1856a86a99fc3a34cf106d5963d65207a2d35e2bee30edf947994407ec912d8d80bf8397a9660ac69ea906e6cd4d0a82cd099bcdea5c22cb94ccd2b1173cbc59ea0fbc8719a60314ecd535326f0dc45253d370c5a1e7dce7a2d1457dbf59eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xf4c9b39d82d4240ebea3a6fc6d3eb294fcef05bcfd4a817656372bb43494b58c39911c689115a8c27dcd3cc6628c885779213612cfba7e4e57b7f5dff61d117001" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n10-duplicate-id.json b/consensus/common/src/test/resources/aere/registry-v3/n10-duplicate-id.json new file mode 100644 index 0000000000..91961390e5 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n10-duplicate-id.json @@ -0,0 +1,62 @@ +{ + "description" : "row 2 carries row 1's id", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-10", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf9c9de83a646dc7f78f23792e596dd3ada0989a9b24524300fe109992fd619014ed6842cc68ec0ccb505132b51c98f72a718aaba11c1ee761c787e3cc4d96bcb4cd4dbf865773b2c71f75d0ae3384b1cfc259c9d36b50765c9ca23adfc5e91c8208d833193279876f96f4610d8d462a6595a82daaa451f396c996bc5366bce0cd446300f238f1f30c639b4d1162de28c58e8a7214cc52d394f8302678b1fde127beb8733c340db994b12ba951bb637635fda6fb3ea8c62b78f4591fc328721dfcd2397e2699afcb37472d396ff48799ceb9faf3bd4af4616eb75c9fdfd888658f04bd1343eb767c740ecc541f1b95469a6c7499684dbaa3c0ce74afa5f316f56d0628cef994f474e6712c51ed108cb00845f9c5e4e1f4d1ef151babab5877a8cbbbbb8ebc8a0c3966bca7ebb3f59255f7b0765f31d0c1fdce9e617e5eadeb94ca65aab2b8789dfe757cccc7b3bcb5010a64b33dd3d7462e7467951f84305e1ca379076439bc897b77944e1b83d1cfee4a3438aed40ac744a1c0bfb39230a8312e7dcfcd40c0aef08ad6b7096fdc288abfaf5e97a308396e919cda9471843187025be38ce75241e9bb9e68cb3f0123ea83d2650a771b7f95b8eee9abba4910a1a5d0ab5c7d0b33105fa6a7306ff62642bac6a35b5f4611739ede89d9728d3acb3c278f37e0a3783c4c7db9a3b73ce468ed62d8794476f6d1ece9c9817d93d491e135b534ce4a84ea3d53040f77883ab055d9ce645ad2c455355a755ad0a1a5d24fb4fd348d1188d909211248b75a65b8cd44cde264f18c3a437ec8358d14b4b5d18a14096b41d16b0c296b37539981cc655bde421de16e7935c3d1966f153608f2b85ae8ba599558f48f366d0a537562017a411bdb83cce350120174480e1fdf1d3dcca8562c21cb932d5c58b1b3dfc0", + "slh-dsa-sha2-128s" : "0xc9de54d901b6fa6bd74f85ba38a2858f64f2826854beff593d80632adc63afecc9e035ba1eca4fe543400ba56b8d83986910002cf9bf630b2bfd3c67a47210d9bcb1276c5888813a81a016f54c8bc2bcba88f7ae12c7506053273e289837cf9f3299de2d9b9ca7a73d6314a297a6dbde9cb7f38b9c0b67cc824bb4ed03c62087188c6b0116d62be4a49b2087a8150b882512565a1c45ef316c27adbfced543002ac88a304a9cecbfd1acfb9d1a46037cbd4cff2832deb574aa2295109fbb79e8e95f53ec31524e9de3e29117403453b82638acc1bc54f781ad9de9e8fc50b398cc11f4ba59ee8c8d7395961f3f75e4867fdc9abf4e3b53b799befa69ac1473a2b178f709906240ff4c0bd4b65c3ac488106c4eb880e1af47dc92d20cd2aebddb78c8151b8a685db9743295bedbad090cc443de04d58894d1dfe1d7d8e08e8d1df1b4847ea7be835993804c0c60754e42486b8566e1b3b3e47bfa262e752197fc95e611f4015b1b94369608982d5ca8cb9faaaf6ac001e48f02cf5f18118e999de2ffd557cc7ca02aaea2a96f6b0fc5229bb497f6842b7a007a8c3572c77e1b5a87a9585bc34b41678832381e051bab823d8a37f1e1dc09fba0c8815c1c9d3fc360c6d70fb19a7675e7b97e9e5046a7bf7e23e463e0ff38eaf10e9dc466145413df96010e96b058499c27686c4d4918743229d3214c3636b351ca421d9fc4505d139e0f60c7226f01d59cc7e0db46fa80dd1267dadc064bbe960f91a8d06007db6f32f33fab0626020a979fdfc9cd72f317108dc29ce26e8e8681ba1a75131a045ee58fef714671d03dc99f53903835e5020497087aa41f2c3076f2077a442d9a9d4b18bf8c1eb0229637fe03f13519127ef8b5013cb8a38d8ecec5c87357737bf857aad26c69d8d6e20cd5883d7df0cd674a63f7d98720f80ab8f0ddf99b6cc8bb279578cdb4ffaf3c5536ebf29ed45f932cea5100404789368965a47350ea672eb34e59744279fd94a233d72c98c731ef729c0ab7e3b9dce92ba9ab5cdccbb9f194e7a05652aa4fc239406ba6692cee5b1dba9674626d376bdd72e89f52019026333a46b7b437088f9c5c372f2efc8f4417e07678e87bb1970e9eba6d5518bd186e03b5d9ca2643d1214708e180169580ab2a59469fc170826c13e329d2d8bacf3b892405a2db21664557ff6161f2ca2439b0d10e1bfbbae0fdd01e567a78b6e016fb19575a5e26d5568ee3657bd49172c23bcda3e0fc94c458d622fe314e1cbaa8b20cc63cc7da1409a624c581ee0d665f199f24b52c3ea31abb954d86c7aacd79d1709cd90105352ec18f435e9066e722f116a912aa92b9f6a91dd1e092a08d5e9d3cbaf77555005815850182f595bed56f645f4da24f7f654e5393d765e2723632f5e05b48bf2bcb82a88ebe21c2078a72b3ea97fcf01f80b017c7b05dd788f0504b157802e9ea7061b8a81b5df14e72428fa13a19b7dfa3ba8fadc6ddd19821a380fb2bb41f5ba61bc6782c4fce6d683f82b56a55048e418bce0356003ce8be9e827e3367ba56bf17f7a3531d07635d7dd2c2ac0515fdb99b371f9e7b93bf28af94f9fa752936f8c302fe9c5a0753c25617184450ba98634b7b3040307fe7af9d30e138803f2d50f9a4db048ef049f488473e598e6707db264048f931775947e481e9c2b012fe5aa05889acbec05e98783d39b65e39373b0dc59a24f6517ee4a17989e053659896b914c3f7af77633fda0f2a06eafc72eca0f7c5d5b8163cf65cd91761584267b6fd666689d7f6a141b52aa58fc31f64fb5c04c65453ba6262dcbb1d8afcf8432b21beb1d5e10753710c9bfbee37eb3dc0a566d83a2fd5d94906bf9d811e51dc3583f3379ddab34347586eed0c64195bf97450a22bd0740954251cdd3a9c63375215652343b2f2a5bf05b022b1ee722b9c9e3d60cb54ec7d3ca81639ff611fe2c2a754e75cc1f445f66a0098d642cb36e1b199863050b334b751bddc152da95e1b32aa49da80fe90841bf9dd02be330dc0bd033f864d227d5db104374d1c551d57de3da82bad013be5e779b42028c1173c52f7cc31a310853a7ce782ba1f3444107a728fe7263580757d2b86f2adb8a648e2ed1902ec15ce0644abe6a1c8f9211d2a97d22899f7da63b4f5b960a30bafb814a928e814f996dc8d63327c79ed8cc57e4e9332ae200a0249d78ef4333ce97c524ed07d357a2142e8fd3d29884acbaede1aca34891414d066d15b254a087998acffd99698f51b5860961fabfbede5ea33d73182140f38de7ccac5911ff7c5e72d6a83268330aa62487ae26ad1a0f9e9b63036c44489742c9b52cf99a6aeb32927a96e4b80468807efefe2399883c8438d76cb727077662b3116883c784f6c479b2d3c2a77ba19301654426ca1c6a6bcd4ed2df1ae383e89dcf16c06c26fee5c15df697c963610720e76c92ed7644814f087ad4c9384a486060fb8918c14a5f3af270e649c4fb6671712ab85534066070757fea8856297af0b4686291ea6592d3c13624cb81e1f870823ad232fec874cdaa0b63c49ec7e8d25ad1c2d6cb2b1d227ac689091b852383818419bd24c3635625e0523b335b87e0e56a08b1e7486983b1b5e473e5a019493dc07a4e0c73ced0b8dd56bd6f7124216b0c8fb1e74d3dd9be21833723242e3578446bd949717e1360144785320ce79acd274a5074ab695c45d9eb49b889b841ea1605b300a1adda0f18770291cf3780a38b50073c393bfa9e721ffba89dd0a3e84c795799f2d2ba2f70041a13e34dd1de43e550f7ba92496eabce75c91e023450aa37dca806ca9a30cd899fb332d0cf3b479e21bc47572a51b5acf7e181667237c25bd4239c2479a59b47c9c32046889b35394433c30cb96c78226b53ea89a8f07754320ad2237129b4a60164dbe0c21ae7cf53b1dd26231ffed917139a6f8783b3eebbb542b938d7969f007458655d2224cc761f61d41ba0a2ee356847c886ead864802027d58c82ff58cc56395bb1e3c623deaf76e6dc322e9109f91b1ebf35e196da1400cfeaeab77b1c6ef8f8330b4969628b1ebd23f1f8f39e557925367d6102ad8f34e908f782a4e6ddc9e14f540f4ade4d630296e412237e77baf745ec093547de84d0604afd7841c5ffea36be213f3bd08d5ebcee65d1108e3d4907ab0aa491f01e155d1e9419ed5549c17ad24032b79091f4bee3392b2ac3aef0f6090b9234e8edc2883f7b872fe74a422a8bdb790b375abe105b07041a05d96a37f3131c5b907b49fd80bca05159d0922a0b8a1518ca549777b7b8f11ef677fe7fe0995cc83690a4370bde93ddca9a6243acec7d5974d2b47f6712fa3dfde07523ad59a6c945d354b9105a363cb4b3f2d380ef5c66f7b67ae50b4d9a524c4694ab33bd2e93fdd0e423b81e955a0aa52bcb0471f2ff7aa8969c44092839ffab230977cb29b81f44d7c56ea5e2841709898da2b0b6a58e40052480ed40beb2c360f1ff3cc10cecbab23777085ae9c2f416fe8f7474a183d9e01e1eb706220b058688ecc650d9e5c69364eb6a755a9bcca5d9177cc48ea5ee39840ec5489252c5713b730234b59a9bcfebd86b7e9212593ea6a7dff5d99e5f26c486567d2f272c1d97c1e69e5847fa0deaab1c8781f48431ef9767c955ce4fbf2829d562be985951d3257511798b74bdb2f5284087c0195ce3b85f6125b16a0001e8c0b10481ee63be97c15f50b0a97b418bd743afd499a6f1107c2d4af18aa7539c33ac19b6794058825d08666e7ad937d4e10b94fbb012749ddbb3819e98c4a9a9cab016103aa0550a17f7eee535207447f228b5dbcfcbf3752de79bc92a3e9ef2b269dfeba7b1265dd29a3e7096eec1b284426f83c9fdfe630d9da9093bcbcf45180d7ce97fafcf1fb53ff559f53d445e96294c91ec0cbdf8c0d34df645bb1a737356fa8138f03748fefbbae40d25eb39a938870f0ab65e46fceed858e78b2d297da91ee56a7046c3bdfd65116a6afc28c0ed4e26b73ef75ea1383698f9a6de23def22d6e38adbdcc7dbb65f7d4aa92edfa61c5e29806898f3de6d756354d5e5d2dae0e6839eacc7eb8684b7b67ddd2ae084235740bcd8c645ae324065e482ed8fb9e857fca3beb4a82f34b89ac6be1029e034a64787a04eab41d001e5a4916a9b7d994250c1e5f32eab646dac9ca463c2d8b9f0b755dd50b5a077db8f4875000461c677f44289eaee145d52cb591cb139f761c64246ed90a306f7667f7587dadcb5c6274e47eb9acfb12f893dd3852763f30f801daf184a506cb5fa070138643db566bcd962f22d1d32c383a8204675040b887cebe61933526e673115ccfe4e636170569787c119c53a6fd2b0924283958a1450ab2113a69cdd8f99e83a1fd0cb96595cfed5ad3dad843d8538cab3c977314a3f230e358625c63801765196979eae49341d02cca3b8aa5185d73faea75623804edb27722211413d6059ff78fe12c448678ac636dae9ad2565317f5eb83575483f17b5e86f2f32be1306821ceeab3eab2ab7908db00785d506ca5c64a7add4c738ff8f6051873c05351f6eec9fd303d1986fa1a0149b524c9cfd5e834806c66b3f0ca81124e9a6970b83a64af256bd9c574a5eefb4bb66ff9b967b7ebe5d10a106f8d9135b12ee040a99ff20d61f9fb62eb0ddf676a15d29a179ee1c7f83cdc26fd0645d49dfae18422321b0070260dc5e66f8ca76dbe4ce4943a15963c2e565dbaaa1adf4477a7163c8bba558ed1a372e47f84164ee2d3110ad80dd3e608131fa11e920e701c29863980205865344827b61d63c342daad9bb52d1aab4c53b732c0cb1359ad7f1b0a60c3cefa7ddc0f420a5c59f124d1c6c4bdb8ce2cb3e591cdc16166c497782c9c4261aece4f2cf638741c34e913e08f701622c112e9942a11900a7e6373a50cdd3dcd283a4f994f4e8ac7e7ab6fcf414d87d8664bd4b1fc8051596fe09572488f0485a6e63904a46213a82caf4a57f03af2f29cd50c72b2f203daaaa641e03f45ea92182faea719acc5219e6868ba59fda61d32b644600aa18c2677813813f1fbc6b4d5526b516b8cd3b798fb5aee5cdce41dd421ecdd6f3a13c9f8ec69ca0779faefe0afaeb37e6e0cd9127fb3a3600b5db3d2737b44d7c3fac76a1549a7d55df1b4bef3fdf64fe9a094edd84706bc20b630c7680961f500679da9f279a9e20b71c1a200ef06b7ed79177f48e830fd58e3300916c946f61b1845233224cf65d49806d73d4b30297826ec2174f63d8625b4023c9d0c13a924172bb0ea55299b9156c2c517a5e91f089df07747377f490f7b2d1cbbadee76a8e97ba1af0cae68ffb9f140f05c1115fc8928457d0f9ccf186d8c6d07ec6c6a7c7d95460fa24c80e073fc50199cb9ac66d1bd2fa5378dd37160da7657815d81338b718bb88d3881e6ac34eec9794d345e401022d9aa62f76052b79cce25a13cdaf1c8dde90b6b28bb4eefd06de47b606f22eac990484f6be859b3a79db4b70b8837c8b46f48b14a795e073c908a1ecb60558a2eed052095611d5f601806992faf4893f0e9e155a4a396df455610842b1eb3a773d1401288b76bf8b3a0bb1767b858e7cf33a6f59950af0a7eef6ad03525611eaca985889a575cc96038233553bb3421ad82f0dc9050afd08d39701fdb708c71147f4f5e9f4e85966f98666d63d81481e63a27e105d8b81d852734cb2c46250b5d90f008a6af80dbaa0e12494525710a9ba32c245de59d79417fffd9d4475aa5f6580a5c3095021e391fecb3395d1b4d37d4aa0ebbbafb03646a220486e7dcd1ea471979592e992aa45fd4b361aa7fbb263c9e49235baf23daef532be753f9bb231cbf3ca3d31d5b55f0ef3bb9ae616ebf3e760b1d04b5e066552d12bf60dd970809e38b8617826138dacbf1375ef828fa6e01eee2eb1dc6a5979e5f9a8e795af0102cbedb7c51c9c550a8128f3d780c6dbfb1740a556e3fbfcc2a8f0c510bde87f6ed03540fe7cadd5bd256af56065f764ee22c0980561afc392dfb6b0c7d520b4d4aa0666567ddab5d5aa0cb039cbe65343ec1e776bb8e0e69999886eb19ead813f69ca515a758474a31494e1e611c0cad2d91abb68c69914232ecaac0cad8fb0af8b4c18e460bba6139198bab41b405afbaafbfd95e4ccbe69ad6fc4b05b68423d4e49f7d313d9fe593dd472cba497a07a86b77d057c9ef26d101dee907588b6b443e0bd65918cc1db642b082848b7757245ac19466c6dcaff1235baf1ace182235394ea01c516f776cc1c80a6c4a1305b31aa52fc7179e97dad1c0d79f81dd7905ebf305b296393aecfc67f6f9d28a7dce5f74d53b0a9acce05320f53c7788437ce863ba196f3881a0bb19edb4355e772a5539d9b23b2a631ee31d55c5a28d3410dbe7e340dd0b6b6c171be399f5a6930f1dbef0e850204b2faa99516cf19899c93d1ef1e2fafc20c5f74642ce6b1035db9597507f43f754fbf32f3e598115b451377bddec28fc19f8410e48413fa9b6d227f323bb2a71fb7e984bb8cdcabbed3151b2cf19e8997fee8fa20f80e5eb5edc71cfa38b0093f957cebaa569be1a1d29b4ac9c4cb5d8e4a2cf36909ba9a1ac5cbdf41c2595a86d9b33524eb25b1f93e0d4a787f68923917e2c9942f776d3163cf791618eae20df41e3af5f9704d2d586b6f6e4347f032ac981726c95ba72c60a38570885e97198d3f9a3cf8637032de5f35f851a0a3c775452f3265a8b0f567a9925dfc0854b2ade20e0b28f7b878bec3c4c170df454dcdc641f32638ea7766d6d1bce6ab4a99ec169d69673f79449bba8a00e6842bc25be10ebbdffc0d74a0f5bcc8e7ffdd743f3232db2eb10e75417bf8ecc672f5e1b158171993540e2b1f7505c81a4978463d45af571e013b4619181028d63b51f73a9e325ebc79398c865b876202185a3860ab98fbe00cd4e385cb4ea5b262f43b2d6524ba0c8cdc24b7fd9a377a8e798281a4e2eff177a871f0aa41159066c89fdcd0bbb89eb42635f6e360148b25e590422a623e0f6a042629effadf969bbe82398bce9fa1297e26976aa39ed5f3c157985656bdad1149054c1a01f385cb6f3cabc2c534730d9174e53ef9157059ff5156b2a01ac71f01ce8869b0e192c3897935dd59356c95751e4119205558db2f040694a57a9a67aa7dbfe9f62d4cd1aa43ede69222884bb88cd74d91c09eee6f6fb96e1ac0b31f7dc29b58a6487c996a2dc1f6af396ab1c60e8c287cbb03a273889661a0f80aaf7d67e2d2ce5e29e64e2ec60c605803bf28e394d4a611af48d5ae920589d3958285f4287c0f56209de0e46a99bc2b184fecc292a0da8351790f8c029ca2eaec8f195efe1a4b2534b98674116e1fce19e625785b857393a92543e4f276024d9c4053f12985ebb40f2d6d7ab22b128f34e4a7e8f26ec138bf62d75aefbb5d1efa4d36578ef3c7d2af3f9990bc8c0a368ad184d6ba36be7a8dba125491e610ed0aa5ef19809afeec4ddaeaea2853a927081f92e58797d581e8e6f6fe73da8d50c13d37b5241000a92e3224c75f228007f3acb2c38ee8d633e1a587379f497e7b93fec2cbc7286827ad0c98ed961ad526407d97c5ffb17d108d345cb6b0d45abf90fca99610c7ff291dd9347bdb94e56555a9610afc3ad27042113ccd9f1561d9fb8441685fd9be44fbb0f2323ed1adf7285c8e65667aa4f8ebf6bd8599f13efa5a3ab2a9ca97e6963cec0c10023674d623f8d25507a47b24f9a0c491087f84613cdae0a1ce438d832a3464d96d1e75fe473359369a1f46871fe097f46a0047a290ff3a4f8200dc7c1e5ced78e3fad8b7470a1f8768d631450ca9ac8b379b65a141143a9e10daf38a246e21b290895a4d903c2e47f039c1d6070128860d3b531c8e46ab617ae34ecb3429169c72e3f134e26ce56e7d4e1669535f25f521b1a5e3a179e9d8a16688b101e91021aa80f9988026c3e92ada1f2411f2965b00616df0fb9fad76c7dd40f36e0778b6ae3046bf61298da63f6039df8aaad0e86e830e83bd85462590f77c7f9f5ad87ca72da678c36d175d23e7df017681549e103628dd0fb47d507a1e0df822006b6278f5c2787c18427919be5a0abf49a98faf159b07eca7dd08be113b053b811b8da734538205d792e7b21904ef90162da6484cabda74302a1aab4db7bbd23bff51810702b2642b3d964f50f234a657608c2464121d4a33000eca4b30ac9357e81310817e1a2de8485487c2a7342259aa3b4260001180df6b0290a8739c0a9d296c3db5cbae8b8e1f3138a4f1fe3e67eceaae13f88da7e86f471740adecefb267089392b2e798614edfa66fdfbe45236cf6189d2b5538df56e635144054cf4f4d9347cc02ab214305be94f1632c02d0110ee5292d6e5755b9a837ead7ad1db8dc71ae91bfad768e7f34ef53bf6b90b7713a3d1f0d4cc0b6df3db1936ab7306d6020eafeed31d7627476d94bd6765a1110969852bdc630c490ddc03879ea1416dce21a2f364f2551fe2383d121e54c568f8bd480ab2fcbe9df0b8790daec4e146f6daf939f5860c8f3c77ef7c4530381de716adc36a7ac284d8dd7ba86241b910e838869c59f5c72431181fb37f8dbdb1d5aee402697823bdd6992449f437bded25af21eecee30bceb02f807cf77afbee810f95a6be473aef683ab1a2ab7ecea32834a44009fa8ae3efa66d5553dd8fb97900468020fddd2d519d66da1f453ac1a6e8c29c34ce9aaf8bbd5a2c59f8e7dfd057a258bf5eda4aa364396145d7e21a63138a01fce8fd7eb259deea64ee92181bbe01de69e7ae060f312b2230bb948aae99cc814692abb6488ed25cc28e8c62f3ad5081b0ac4acc7992e233c9008a1687fc843fe9c0352016ddedd624b060eb62957094f423332fc72c6162628b27f83300c188e53aa078d827aab56b885cfed3ef50ae5543f884bb2d860a051e6d369e3e6b2d30b7abbd9e9564bb123955f1d31e13181e963f346f558fcae1f26fb81c90be77464d63064656ee9edc0d08cee1c2797557f90dd46c8e0dc852f4a11119025b346e1c35e735dd739f4ce869c7583c0819ee748371799d3292c5eebfeef3542a08abf79db73945878520594fd1e79f1d57a22cf288025c9990c0801190ff54be4df7ad92c86e886ef08d81645ef92e93f666216f9c3a95358227f505f3c586f5fcc5e417d8847b91079c5fbd2b3289ccad9c08f4be5b9e04577d9b768da2d7b192d4217f14a8fe31869921f8cf899c744d1857f7e66fbf09aad0206261db64e8285d86cf5050a591c6ed0cacb138637423b7bf44a735300c8d31d4ff434b10a6ea142595ac0a3e1cc51d87728a56ba78943407909866638164ccb2c2fb241d5b7943e76dba3f29192ad5d3aba4027448ea99708b81a06a15feb3909325c38841f0bd904c51ad383b55eedc282b7350435f75f2e3aa691fa8e24f0318c4055137de195ba0432772ac70a2cd0cd0819b55170c610862cee136143f8e467bb14c9a548c0f46166b75bd0541749b3fc486ed02f774556edd410f0600bbc784ce32f1732d58607867ee583e584b70df8b276e98a72a09873668124fe3b09c403fa6b3d30087a64d551da4d373a9fa4256f0f2dafd254c14696a0c4dc1f2de33c67ec3051cb606c5dd37c9ed8d03299d7198d6d1b8232d2ea7e0cbb8b8a3acf144d110a04420286f8b538569be5672f3c6b93d8343f1f9927d72cc64733450d188b8e82b9b30187f68751bd3579e4d035965f4d337a600a5304669e27207ba5f80e5ba68932749174018fcaac4b5e39cbb79dd0cea07f2a3da996ed69b5d728992c67025c69f7a3ac5f419cfd267ab72cc91596215ccab718b4db8706bfcede4aad669c91b42537991cc508282c9d4a8c2c15003bdb810716a02f3f08215dbb3204178e10179373b822107de5e3637ad23463efdc6b18c6a5ec7363794c971cbe338b9db08f69af2912b9d45be8396a7f726d78e6e85f7b4341c3b35403ab399a7181c4b33a9975a1c4d07ce6c5a8f8b9f614d4638db39eb05eed248b63b45440af6d790fed26d77183581bd7129e7e03d61108d81f590385f2c413835a11b57729f6fff470b56e78f54a2d73a2d104f4994299543d8783482c2aa218a31bfe3cb2e68aa6cdbf2dfc9b75f7c55e3005979196c4aada77490aad5c52c2493ba4b08cff6b17c0614c4673f902809512770539c7ade6ba2cd43cdf9c668310bed149f467c0d44ebbb76a23a8fb28fe7fc2e1000ea5e8c5e61dff237be9f0c2ee0fab29114dc07b229a93a2ff02bc7077eeecf087f3716fcd69e64ed9b5d9b78b8b159384d51bc43d64415da2a4f29410459603a4cf6a6735bbdca3650fd15aff192a785a3ccab7f3a4df74a02d724d36d4e98e243256e6361c50f4b9839e0e73bab1ab3cfe3f8e2a1a94a7734644c2e0077c9c12614db21f024ecbb048c15cde54859e93e73ba07df2b11eef7d7ad9485d9a8dc674645510547b6f0d586f7beb8c2b1991b4e9794730fcd31cfff08743f41c82f1bfd11f01f10af1c2a8b7d4e5a586016f5b523abdd11d1bc77d9b678920fbd73c406db701c27020f910fb63dc59ed44ef7de5676346989152da54b0073228e439c32d1ac85e667ce2392278ea1d528bb9828287ed1ac64c91413a2d849fcb56e5f08a070487730e25bf566529a1f7e4ceb037e10fbae016713069bd81e874950f6ba5c1b2f4eb26d04aed24a1e64ee0eef339f13c5c55cc229da57c86f676103c10d20e5da578cde3f2bc71acf2ab1afab32df621cdc451eca58cd86c9f55c8fe41d056a581a446bfac3acde068e930583e0fd43c51f8a36677d23436e48c5306795d745aa3c1679893b9223bebfdde35beed183de0b663d9099eed1edb3b7b1ca9eadd7d5b73421ffdc59ee0a3dac1688546fb42e78db833cdd1706d8bcc91f3eb0c66ae5834361cf78f8122b95e48e5b8149b46ca8e0338205965182150ff69e0f7f129b725779e9c7cd83782da6e6a7f97d596480369866899684068e051fb98e5ce562b05992e27a1358d3ac495300f21f474e7ad94f8c0958c8c7ae4801e39ffbac70c4ec3fdfbdc13ceb919aba3822b1c0311b115c861290fc333c32aa74f38f7ee4c1fbc3f1ddff350c7393a48dcceb1e9ca" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0xdc947bf1571de4970daa8a731a94015700b880a4b5f0bb1a06f50a8fdf3282452d45e3f3052db6e09410480b99bafb7d0f887de669c896b4547efc64ef20446d01" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n11-duplicate-key.json b/consensus/common/src/test/resources/aere/registry-v3/n11-duplicate-key.json new file mode 100644 index 0000000000..d1e7730e74 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n11-duplicate-key.json @@ -0,0 +1,62 @@ +{ + "description" : "row 2 carries row 1's slh-dsa key", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-11", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3974a283855c2660f08a7de343c0ebb15dbe2963c3dd6e5167a9214aaf91483e511ba80e41ed58933631744da49a2aa9406f729496c3858c21128715719736ef2ef8a6f766308f834b9a8bdf37a9acce248e95d78e5acfbc0c40e8af2c52a0f018e3a9dbe0f7198c809efc71d5ddca55115c7076687a2fa34e3ed98c2d53697344d5892c3704416d11775492e393d93f8ceebbc8bf00ea6f8fb413e4481299a74232e53bb08efc4634e812a67b11f3c66f394c2c82857791aa912bb7960160921dc630444beb693164b57834519e33682542cd8b47f22a5562a0bcbe046512c38fd4909becd1c896d6d5cb96c3dfa413c32e98945cfb85f8a246899f6ca2a619530383d665583c5cae30bf4734da866f323c96a6725116ded3b79c527b9ecbc166753829b966fd75c454935fab98e7cbcccb2385011099d6762c320493e2d57aa50d85a9358b5a34ea74258ddca98c22c3255beba5306f3668c3a1bfda3a7da93ea9c27ec42b502647f0de2097cde4b34c9daeb5c5534ca0f0cf12f0d1dd4d8605d15bcd02d58c6e6e51a5436fbad2d6597ae2dbe7e090c8c3928ba75384bd09997e3f512e03ab81d4b24a4ea71b0ad1d5f5f16f72950046618c0198df20ded31541aec2bc0c8a1b38936a595eb7e30b8322942d24c6b138cfa9939647c65bfe7642ce716aa7db44689ab54b2badc8184641a0753f36cd32ed95f8467345df6e99274c5ab0f9725378035b2592346733283310f168b0e14e423baa6c917d2cefdb2075a48d0584785e03b2e8213bfb314acb11fe66b297c16e86abf390244e2b2885bafb7641ba86e20649f4bf4612f936319034554df58d71f0e7296dfc6f74c0bc0f5ac9869705173429b149fe47bdb29c27194262d94f62e49b6bb3e0f5d054760dd3d4e31f685d8e766be0ab0c", + "slh-dsa-sha2-128s" : "0x81709f7b1144f9f5dff017c8404c3a0d6915874e412f3a49100aa094998baf781b3813554da2507c6dac69721c353bde08d7916e70cbcb85c8aebac92c41a420bb72e3239d878ef704089319f11ed02fcc081216bd69c01d5c9f1b2df5c8124941a08e0b5bb0e001eb357a9c1eedf78ab65e1a0d3c37bde0b04ad42f051f13eb690960cb1f0918f3e2c6bb8d0268d5b152426bec82cc9df9c0859effcbcc4bdc9861b188d5f58a39f756dad13384b1d715df24d5ae8d6d867a14093417621892954b0b003623ec7effc1740d1022d752626472742b914ab7bf77131a5372d452901bdc13e7478525067812025da47f43ed6fea5e28a627b5dabed2dab313bb0ef2575c9c1cb4678e3465533086f11976c7210ae58968ec5c72646fa52cd54f61fc0d0f22ee45e69e21e25ef06feba51083454ff55a8fdc25f9b77a8348a9e0b7e7d9652914bab0446d10e3d449b656d27bc2d2349d559fb5c7e893216ad6b05cc5d3107125072f9495c0b72d03c0137ba0bb402dfdff6a5f3a94096885b9b2aa63c012cccc2f10496d9d5de2a044c730600c2f1bef829cdd8128f26bb0995ce91a54e3ab3cc24b03b482e4cd342aeff77defa4e1390149b04659e5d46fef266474aa89a81db88bbc7867f80424efac9a3145df2b44b8a70b10c748a9858e77189fa85dbb0cda5c1a4db3c1a585ce3da4241d404e4926a4ff5776357e32d806b632270cbef14615aed54ffe8ded20de50301364b46bad87c0b00f6f895a8d41671ca473ff978ca5c23851df6af08167916c5eb0f85eed0daca0c8cce14a307974924a44c458f7993a2d6fcef3e1935c943bd2b96038a525cb801d25a337526107a5e8406e1ba5d5907593af3d702d63bc7a4e966652cf6816da5c2dd3ace68f5cc554794affd42d4c64d022b54c9ce32802142f98c742e1ec7cd18c8079a215ea6106ddc76a79705db9f574a45be1a0f5d1bfa91274f1d560180b346c58ed068c2147b172af30c3b85f3aca5a4370a5b94142364d21bfe28de8012e252f75b69aedb4e41053e11f055805991cf93a60defc865fc3a1318418982feb7f7bc24eb22eec05a0023e56a72705ba4623c3c9187ee71502c86897d82d1e6c962ba97646db58756bd1cd934814d26ddd7ac0d13ae74c0654c1a55bef9eb7474f18ea1e18776f1eeae615085cbf7c7cc807708062a47320ae195cb20e7952f2d357a9c134adff063a276c6fb08d7e9a435ae0071a76256f2e6f043f1ef26a57f8f7d92de7e81c3c46c26015ad3aec2b8d789e9eb6e3800e3cb4000b2ce19ad80193db8916bcc3c22c78060f61dbce94f05da69df9cd7fdc56b746de15c3eec4ff7b194207cb6e467ecb518db510031abb9e36683d4e6d16d9f03386c017855425361f027498e82c0432b315a643ea873bd9c82a60a699547cc65247bd573760f3d63191cd813d2dc4eac926fe93ad981239bb9b5f1decab35d805e85ac312f17e2d925de220e3e34a98a92948e2c5f7c8bd7967bdf26d602ce474ddd799941ae8729b14dcab428d80222960f710de03792a21f8598a798a2f4154fce79e7b405be5f2c5f50c096eadb91c09ad8b97173f1be97125955d8b86ced4c525877f6a4727b9137bf351cac869eaa68264d3f348f6abbe7399d42c8d9f892d8ee47750ee02e7904b0611d46d5cdbee450765839a99304493d8979088b2e16f663c16196201059ee02328d1716311dd921be932440dadcc3b00d1aa4f57bebae2c3769429db44709e91ee8bd244adac252a0a56b29846b24fc0a6d81c14d766f4f3a40e07f2e76be8b408cc4d380a44d9ae009248c033ca792cbdb3b7aa7a5c8b01ccf950456787a3ac0dc915747cb81ff17bb986bf30841f9c7f515cd506a0fb87a9dd8558ff6b028be9d7b87fd449519d1cf1e8dfd7a93594645b635c469bc1a64a6429d59a3f46d495390730f4614db293757b71e8a8c6339678190a1817f3fc4a770b7f60aac2f36757bfe4fc7e6305ae6d2c3fabbf239807079bee138c209b8e7919c6d28c89d2c95e229ffd15d0faa70c0ac77210327e0c49d6d3819fc0d6bfe25f4369685c12c9662d45b464c5e5c241a26127de1ef6afdebe372e9ecdd3d1a94066eef2bf33e1cb870986e0d27970c4decb8b5718bb0711fdea5ae35445c54f67b42c8317426ba4a2a2f3fe5bdf67f92b4acf8d729a68c1362fe91ce613e490793910653814efa47a7db6177338e832eb363bfc4c7942e3c8d06cb5984080cf8f0acaf8551f18ab36621fa6d4ef6973f5a927690e1e5b3eb538f3a6118280cad37ca9ea7cfa7e5a24b5ee01fc98b07bb02741f7f20e135c2e20253e41c8265c115894ae33d0f18f294822ce5a4b2d1895bde7ec205c548c52f3a33520277ddac099cc9fd654174ac3a9380a3cf246c74dc3e847a4f4c178a79024804ac1be3405dd5e1b4f1e5097d7dc8e49c5f6646d56b4e6b70fbb7a6bfa2a42b389ab0a67750f2fff0e1caf18c7d46e05a245c3a45357dd97699eb40559916fe67953f702cbb6603303f37d6305cc6405b7f39c05ad63d162a91f032e245b3cc6b40f483cf5a5bef09f31eacb3effbfd0fba965ac8896e16d3c17646fa7bb722517eb7fbdb7849683d8e082901209cdcc51a9fdd19442267acf93752935f4e981973f7c9137a0dde8f241cd44f4188eaff6bd1c770f89370d457e3aaa1017545ee9913c8e821f998e562b7ea72aafa9d3983352c42e3224ff6a1134149e6c0972f3f5b9e798524ab5d7c956067d10d9197035d02823e800aeeccd830ea228734d503036f0eb3427517ef5270328773c8c19fb10bffd8b78dff1c62812ba24bc9bafbccbb1563754f803196797612e983c0e529d4731cd1b8c4f325b9110241a49d2a142b07b03af9c8c0f21e6c7560d0ed0d7b7fb76959dd1a02ce9af31a452947404d355d19093e4061524e6aebbcf8012ee88680a5ab4337a3e204517cfcf02e5f0b7dd17d56daf171ce910d1da62a8baff52b3c432270eb31ea000948c77087f9a16d2eb5ab0a60113891900ebf3a4fd50f5fe6492b03b301454312cb33100e8f7171cab291fb5c2abc7289ab390616364b35ed7cd8fcf9336053c3e6dfdd4bac5ed2a3f951bd24b599e97d417fd8d3d75c473a3fd39d60cfc60cc1643dc3c9fee812802992ecba7fef5b83a8f835c3605aaac5dc6b1a30b8ab2d3f7cf0021ff17de965e98945e65d6ff8608081b9cae901f8db3f5f4795e1943b500e2d5c751e3c3dcf6e133e3f03108f89b507346daa4e793b1644a63df3cf165b842ce72b6b82c4d2b34cd049d74f464d602de8e1a17a1126aa881bdd1e46494aaf18ca7d2771214f24810a2491a1bd5340a66e422b5caa0d99af2536f7b0d61e050c448fbf95cbfdcde26c011b4b5cbe0ed14706ab310e8494c6b7de256a1084a8efc7e7c46425c48fc11669e3c59ab4275a17b97385fd6abe772eab55ba80485b23c07c994e997e7792420d164d2f56ca81d23af5e330ceeb9a98326b102fd7c4928c1aebc07d85232f73283df607215c5399232397c27da072c00a4d9b889a5335425c8402c174c4a82e46bb47e44582c887efb8018858decec4827a1a8e0e51acf554dc925a759ed729eb218a770be9ebd40152ae05e8bcab0306280bf2e441c30c3e85cbff7c4dd80569081bc11b1bf4e901ce1771c5f9562a2b5f3e78f04a831ebe744d1f14471701ed243ea474b03448e4b894c1528298c9090102d69518c58c49bd37e9d09b272a0506917c0d010c1d552924a89b5e5085ea08050088e3bcc6683d277c112ad254a93bf6d593e161a0f2dba3acebf175d34e179e3d4affa9a67c7abe77fe741f86d3614f69d78d5424f06c5aa592604e5e17b325811734494e9e08bc3182384b21c8f9b89ab416125f29b7c5492e04007e7d257efe01006b9e0d9d2a5ad7037286c3fa95ca29f26cc8067aed4a6b09885af4700bea93c8f3971d617b04dafb53f2cf349a38c7ff5ac491f0a3de1c62b24234867985a190a07ddb16ae34c67a28bf24f3e62d5aaa70b96866e5eb545098ce5b4b2ee3ecac0ea8a364c8a1eaf2f9c15c4a2f3f8126339123693555c7bc64e506a98b4b9f0d77308e33f71c6a3dcfc2bcf7d331d889db6210788427878c93fea18fc3ee7e9c5c416fa3125630096529e90175f8246f906372825ff87c433af9fa60ddbbe54a44238278c3b2439b6b572dfbfced008765d1dbc05f9e5abdb01d98d14e748628c3ce197162cb2a293b9d7cf7b5775ab52a43a31d25fecac96f0daa371958f226c963abea16f280292e45375c6a4caf980cd84abca8f280c31e6355561b06b90b98efcb7ae86ffb1b4c2fb829c7bfc02d63b146a07af6b2c70dd3fea0d7e3b2189492051f825dd318dd586b88b0f69bb4adadcfcddb616ac24b2a6ebbff7dc4042b6b02872c8c16374b96bd5f2adcdfc75e94c743eb0bc4abcd1cdb028d840c8c89b69b5b917057e4f6b90055e6f903d8f023cea016dc706ee89f2f654324cbafb03b0b4f7016b0fec4319a19596e226bb1d6cafe41cd0ae4f1ad9b54163814ed2cbf1a6008964c1a6d8115734d960c0b978ef26fe911dcd1ab83db34d1f8a5e5288bdea20dad9f5e4364986ef95ddaa1bf75a37fe30ce3075c93652e746f49d0981b5d631fdc15baf0069d57bdfc28a27413d04884aa306f2c9db7fe288b6056d9b9c132a95a13401f17d924bb7b7ad44dfc6b371a77b0a23ce40045472812690ae34b0ee6bf7056200971dde6a133acbdd586dfb91df14d181093110b22d3e13c06adb9e5eddc6838a14ed6683beab5910fece340572b3109c9d09b4dab95f2aaa5c286cbcd9a413ff5b0765bb0c912dbc717dd13c7fe46b05dc0417ee6fc81b011074783b61f7feecfce6f8f4cc87bbd988430bc3ee68c9d06d843c1d51ad1f37a40c73d448786eecafaac7fd84e028d746f6b0689ee61e93f0d436eee6466f1bfa0ab0288a080a817eb31c59106217efd5ba43620401db5042e4b0ea0ce39f9450decf2d4d69937ac08b0ac9f538f3795d24da9c12f8e781961d1183edad10fea48279972760486a470a640966e091c7ad8c1bf435fe15978613e188b4bc5e0ed8afa0961909928d35cbebc8284110956964880a664196a355102b21e4930f4cbc74aaba3000838a3e84182a486f8f94a223369adbdd3060a424e0eaf79e97a5158289f513176c8d583143d612c7bb951c040bf71a4f9797463385bb9a8934885fcd3b5cd28a8296fbd16e9eefb600d5e9f0f1e61a35f202eed5042da1cc4631b9546cbd27b0b31a8a764bc4950f3bc2b725983e29d43344ddc394b7d531f1a3f8c299ba9b9672abe9dce3244aee6eaca69ba7357a046dbedfe0ca3ac137cc4c68c9582921a801df2c10f4a62c5ee6837d93e90c346eb9424e027177b6af5d676734295adcdf9ff1fe239b7d8ec5defba43a365927fa6d055375d23132e2e945f33cb0edd3c0e519c039b489a4bb5eb4b619c6d80cf40fb87598acc9164b748824c409573b2d80880dd8ed6f67f715178c52a6c4d27f6f8b7b183c25740c6a81d22c1d88a9b13d44feee20cdf900f39ad1086c53a2e853d9c8c9e987d5b151459f921782710002c602fc94c7639cda910268ebe823d752a2954289933f99bb6f2d5eb22b615392ac4a6e5921beb51a0af757cea3ea56d913faa1ab71a0601a0664d3beb4e6a7b0716ba6bf5e720662376d3a71516833840359a73a1a868d9da700d47651efc3490b0340226d8cc6afd5ff6fd1f684120ca9c444bf19fd7f55a159e06bdd202855e2824311994eca4a206478fc7e1e29fec619506e43ba77feb8c159f067b55a47a2d0fce452f0f61b2a746fdb7ad963e0c74fc06243b650496f3ea72ca06f36813befad7c20008d2fcb3eb4db2799a70e6cfd30d499b16154d86a1f1714e8f02827e367be7c06464eb8fb0f75293ff2aa2a8a04577043b60b2a41492de40a235b69bc6006e45fc651301fc300acfce3494c18710fab004601cec07a08d86b466c6e0c9f156a647a9787dc26c483cb589d2f1622b31caa07f32a2a878d9806ba3dee087949d8ccb3506a3110fe717de36cc41c74046deb937f878086c04279e16135a4f6a8c824af7883b3e22958d9aafd26f0ac57e2421e2722799d5638a2f42311a1f93869b972f6bb5f23e7b0fcfc8d15b4b5b29eed5fefd8a8e33d8bafa4765e08a1f00ce21487a5d4801b221db87e9635d63035209bb6e3d5c1835184689dfcb34f3864ab8f409777f3d335501c48da98d6d6bac1b683645c9e6ca9a0ee0e7dc57572b0051b23a5a51b113c64e89fe256dfd6143d9ff487030e6d48d8b0288ceee6a1c66b9a04dcb94a8e02f46954271d55d75ed61f22ce4583cf1899c0b00d8ec509d20eb1bf3663f44ad40f2d2f462a45c81322e3b5786a6a5318c9f1ba9606ee75f119ef398822765f548f268f17a831dbd5f20aa018e0e90ac6dbe4c353b03c8814e3823b4fddc513ce201d987ee7691f682ddce6fe19ec530ee5a48cf4a14de58bea44ef9dc2d6dcd9e78068ccc55789b8cda917d2321441280a86feb3a99d965cf894930607e61e3aff981c86c635718dba067f2ee9967d2200e453ca6fb7399e31e6edb5c71828c1e9afbbb4d2527d4308ad3926bf9dd9bf21d0a09e604b5382168d420f7038bdca5183b3130121260c20f71a79ab839ba47cb00451811a3e4575700e0a9a33c9a5007ec15e29548e6753c4674c48a7c940e2452aad50b2de471d7df174597c2be8790fa532919752b4e25aa3cf7ac5d24ce3c7dc29bb9593e0a6ba3d15ae7eb25b962ad7070aa0b0ee6f5752d2ba75a6667809d336b10fcc64b076a1dfea563dc16a15bf81956222387ebb529f59671928b0363e48df63cb753ba4acfc7d47c1ca52717d7f39e165b8028e6b4534ff532d50ea6c4e53e2edcc3f030168c9f643237286d4f4d79e6d0c574826d04432267b3e83b6d14550a5c3486d6c4f9664901bb7ba5c8eeeb71f9ad7564551d4fc2cb2a9887b4b67ca8bb47bbfb267a0fef98e0065689f3cf846ea8079fc7a4946c4775d47db01fb42242461b90d8ca064de876cceb1c81acfb3b0ae1df7884f22ac23e294d1db4a34f51cc78266c2fec31e4e302776ae004c186f935c6b4ea0848fb535a53a192f4d7cae2519cda8e329257864569aa89d95104cae0605e671601b513d3fde5287059e51e133b57fe822cbb3ec8cb211104ca8eabbdca781c331d6c6cbfde5d1e07a7e4480b9bc899ff81172bed01a17764cb4903b4b1dca91bf9766f5a017cc2cfb80b568169bfff2a0faec60c68bcb23466011d98e153423aca4084870817381a156803b032d0cecdda83df8cf3b1ffdce232cebebcfcfa3334066d4967726e10d1f422303c97d8ce863d3706846b8c3d611131817ad117edd929f228924644daa9127383c5b5dc7c86415f7a71ce3defe42c585954711d85e505d7da236e3f89e2732a5f1138a3228225572252a57d2a264f708f59ce667817426ed200e12138900520280887e4ceb1e5b35c4491b42c82dccbad0e892773e99f7e33344bc03885b55e812089c70e3cf2d224ce08b767a38c30c5dc0b83a8272a94d499d7796e9d5baf2511f5ff74a5c4af5943c0c450ecff4ffb7b466196894b2da7833df08979ffc7a8414960da90d02a64e266fcd1570b5666608ae345e9009abc0a3765eecc1bb4340525b53f145b99efcfc148f6134f61e99d7968c06241b5f8628f793f58b26085ee9df53403ebdd4f93278be4f108c3e13b5d34998fc375bcef2e31bf99d7b3fa653c472c11deba26ece9e6c2f9376847bf167a7d98518dd90f586bc77db402efb7e7a8eabbfa1facb0401793db5de20b4f4b2c4579017b2ed826d1f09a297819d735918a23de4eb7383a21bb90f18fd996e2f04bc7f207d175cffe2c332f2fa0e7e80e4383fc8f584d725126f382025da0fede837f5fe4dc647ded43c7530c1777c2c5ca2091123673b49fcc248ddb72bde28d557f919d0ffe93354ee68d854e9c5823620161e616190fa0d3a32f1d106c576d8a38dd64df3fb03e3d0fef034fe09b438d1fe8800bf5094f184e29d736e94d0bfb410cae39500e253b6c973ebd24c2950270d85f2aa71408440e487618d53fa0a1521de9e3e24cf7bab8b43e4a464bfe1fdbb0bfbd443d3ff45dbcaf761ffea019f1d835e945a7e06ed09e945fe3278fc3d3154d418c0cbc82612d3dcfb029aa807087324c6b46b60ae25d061878df415153064f83e3ef6da2b2149785e41805e5be0b96f61d315c5defe936a649d5bee68cf92f831e651e0f588dc4d86c48f36d5ccae935d40e8b0cfe6acb65ab535715bfa87a3763c4bdca155f7e1e1ce65dea089d7866a4b212e488223918c60049263a776c3a934af1edd04859edd1d393df0adb67c455a56575b44eafddebf6feb70910c720cd771cd1c09866f493731e8cef9484180b0ef67dfc579e21cd55641bda6981b1d5d3e2b664327044fb3de9c9b05fee297328e54eabfbe9917f3cd5ed9855f9c93ecaab0ea8a9497e7e1bf35322cafef0ca95a117b62261aae081191d67ad7a915f0fed459e4d27e944d8ebec31fd6d2062956f5c833b2a4ccb30814dd91b5a8efe9c152e1f5e68f41ff48e925ed9a0a78b33b43373a64d9c87ab2ad01b3807f38e72d8adc21808d831a888def2895e6e7acf1527483ed2dfe065172fade7750604a7ee62200c486d9144369a2fe72bfaaea3d7e3981e124819aed1c36f0dc9ce6f5b36bd4c5018493dc91d1e078bcb6dd0c96e46f83739ccc7cb7c9f2dc88edbd988df1ef4fe58d640667922e9a49d83da004db34488d09aab0cba8e2ac141648ec4cbfe90853aa3b94f7055522f3ff8c83a25e671456c977e0183d6e9d56da437dc8d60efe9f6f8bbc094b0f79c754612574ee8901eedfcc90d2981ff578a48856ebc35150acbf0538df7cc5f5a5d620ca47bcdae52cdef2952d8486bf23d903c400374ef6bd1dd9f4c1760d3824104c1f37cb7811cebcd6d18b58d9ae580d781dfd3f47b6b1f43fa8d98d7315fdc844cd40a6a3d9c3e55b4e655ddbcbbfada63ce0c7c2f174021ca80df9d692ee2ec6c68df4b20ccaa4305a95ee90b42dfe8cebf6a504a6ed504525a1d63053809f6e38b334013acdc899e842f02f703963ef760997199784fe9e426d31524af130e15e350d16355cb69985cb6989017a4872c10b252a940916414ea9bc84105560801ac77c7f400132b5a192365c1da6025ed7668f889aedf4ac2a664afe2620ac881dc072fd5edeb87b0f2ffc1c0fcb27ae9a62835292f0b51611de58f2b3260675a11998de1e3bf626b7d0b033c8f148779c54d9524e420268e949fa1884d56eaf0bafa33ac12829fc3d556082f5644a8f3a53c4e6181fa9831a404832180f06152ce01ed24682bcffe60026956883b050a4c43d33cdf8dc56318bd76021318c2427bd19eb600badc4038c5bf760d461543b36de718e4e91a0850ad2f8ea82c265842f614e99d70b385fec917dbdee81b227cd0adc0a5f4db5c4d268271fb739154420bcbb2ba33303d9f939da5d3f2803f5dbd2e02becc683b1c5f825b2a2c7332e4bd3fb9b41d4b07532c2de5772cec38f5e2ddf6f9926f2cd2c63f752d06a407723ff1ad4c3e0d11565a414de52c7e8c46bd913f9e2c0a53c2f731ce5851b1a14ed968a3fbbb9e8a55b93aecdf2868dc81d6c77a8fc9ddfd3ff43799034e7eee170b2fa9b0e7ab5076c35e5f3db3da00100d0712d05a8789af1eccaf37443408634659bb67f51a93057b14118021197c77ccb096f09a5102dbd64af86fc10a6a4699c8fa0aaab6a7f2ca63e95d7d950128056d3cfd76d6ad7404190430ef77803b4cc477d2d2ea5403cfc306ce3ec547d79f1c5f41547981629360f40158bc7c27215804ebd4f42bd8defb920da7d60b15c5a3641aa4f56dc54a14da0fcf7c1c5f238272fd75d70ceed15847a33564845685944035d55488c95d9113b148d434714eb4dcea337daf0b1dad4ad5926c5cac406bece6826b4f335b1b02d0538a32276a1df31b5c378f83f248a4398ac18a06a136375711a8935443471f44758dfa462c24ce9169182eff1698475a064c697b56510cf2dfc77023596304fbedfa865a2127f54a3fce0c2a8400bdf3e106b49edd53dc2f4414fd3af6d62ead68af57a88216546731edc26c0717b804df554ba3ec5d7cc39bb68613d99c6ac4e50df2720e151b37bb1f01843af17540ede0cfcfcc744871e2869f776c78ceb04398a7c9222d76cf7469852c825f53ada2e39c521df4240ca70129ef2d123f3f6efa138808386237d1b3b5a4dafd4429d143fe92205329f2062331f775867311970038cbde439ce6fc5425dd713cc4ea1bb5d0e36e97b75c031b138a3170a89d7107e795a0e052f56d3731fca334977d11e6e0aaba784f4366740649d28e01b9a47d26aff23cd6eee2b3a0e57a9cb8a8a9fbbb81a608a6444f9941f77757e52727166ee1bdef8c621a6d27e776f62b24eda9d1e1aae160280f011fdcbc45f995681b56b500a967de5a765c8f6f80336421e96e90cd251344e9d84f83cbf09176e29dcd15365f8892130bda24975b70df3717504a007a8d64bac7b2644931375a56df1e76fb3554c87cf6a674baf1293462a8140981fc7ccba0cd3cfe3c002ba9d443da991464dc27c85e6c599ed8f3eae56ca1a8aca810eec0472318fa7734535036292b870229e44fa3c6bcd79e2d72930b8da76f54b627dfe2b572db5946e49e482a128e6ab5b4cacf1a8480f4b2a89d889d22110bd44c3823e4359d9b21edb5dfcd65c968b17d2a56729f7bc9591fb5423a51d3173b1cab3de487564619eb4975af7d1882374c8c1f3652c433cae0001796b37098d62f9f5c8660d855d106388c38a7175fd503bb092e1dd3fbe9da79c977588f62dcab26b414620c25b6b50a649988fb6f177c66df6f6bec3e9e2c491e00be0641974a86fa730929869cac672a45c497cf7fdfb23fd2d4b9bff2551d98587af13867fb17fdb1e77d95b286539260631bc800bd27f3cf3f6904d81d4cb8a19a924af0dc45253d370c5a1e7dce7a2d1457dbf59eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x85bb15dd7f350a7178cc3b8ea7601c890a82b052c1aef38b249e5455de31d592531031bce1f03b71782bfc6293d7a146f84b834ce5da977efd1c1eae03c0312701" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n12-rotation-one-key.json b/consensus/common/src/test/resources/aere/registry-v3/n12-rotation-one-key.json new file mode 100644 index 0000000000..898f5b344c --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n12-rotation-one-key.json @@ -0,0 +1,50 @@ +{ + "description" : "index 1 rotates with the old falcon key only (slh missing)", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : false, + "previous" : "ok-epoch-a", + "expect" : "AERE-PQC-REG3-12", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 2000, + "count" : 3, + "0" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x391de378d9ee4e1d810e6ce145b1819f1b73dab7f6dc2468fa65d1ddc8e2a7e6fd8f185f8945700a938cc0c5afd87bda48c1b8167c3e6607ac864bb2b194a368c33cf8ab6a5eebb9a704d4160b068184264c2e59c4f82b9b4eaa9197c3ba5289c429e0f4c548769279daade1286d354212b1ba2ea4818c974c17770dbb59b65b3b9f891c826721f51a7ce14f94e2ef4cab868cf852ed9bfb17cd33ebdc4d688d6ba12c74aed2c8104d117574642af25e9bc91ba3fdabfdf1ed13c6e59fa1a0a4388925d2254195c666a6c235174a73125790bbdbed463617757fcc17cfd32da7c7f89e755f051aaee186665d9de393a82508d15f88db0271495c959bda6651a81a04dfc2103364f95f5464effde48f3b7ea80af9c1bbee99c9d39b0627984f09ccc6a4c888d121ca34e56760b1c3178e48607325676c484f4d666681f0d2454f7fdc59cca37136c5c06e746987e6a116728f8b0ae1ae9a32efdfcc956fefa55577ac670bc3d66016391bbb833d97f91d3a78696370a6f05efbd77cd50d4697c3df5699277eb1cafc1883776d9ecc72a763e5b65dbdd184efb9696cf58360e73f2d4db5e8bc6ad65bbbe7a8a994f5073fb4b12ad9b5196c450e27a53d8ca231f670966a238a6223b47d90990b1f6796f93eae971d4939d99dfea29d4ba2e3feffb97c0b27d6b6d2ee10751e75c658b44ee71abe9230f74a0d7f232329b165f5b2a3e584dfedaafa9ab477c0c516b6553c58797421f8d1e9236f992b61963c108752b1c43278a9b910926b396292fba67211e9a16b88aa9bab7b6b6db29f30ab5b7ef1568d46a010e8a27144a5e958022be5cf6bf4305f14e10781323acca3bb1d6b567d3fe5f652661dfc54d71ed9e3969289c29bff0c8972899993505cde2fca11c821d9280abfd5c20d4fb53380", + "slh-dsa-sha2-128s" : "0xee44a0de25b75a42e32080200c89b82e2f89551d44da272720c816fb144aa42ca29cc7a0045d97c82676d36286a9f1bf4d5b9e44b6855d90f25e03e3370cccc7665f3c7fdeb6f9565b76745a7652f70b7cb18989582559b81160fa32c7db7915f14c657ca37224955d174cc69de41774e4ac576f362306f3383733b322f8b333d9d0dee44d80e36a86491bfb7441a3c66809bc600399ac9788b4d7796604d4ec423c40dc1a6fb2b02db9a0d35072cb5aa617ef769006c504052596186ac0d504561c815617067f669b29757aed1de7a03c9c0410fb8b040dac268aa95232533eef1f69cc0100b33c9726d55af73b5d608b501b0470c4e9340b68251fc645bca481dc2d375de626580faa7b7faba8f8a596f795481cac23240854839a1100cd92a6410291ccd40cfdf1f70981c568b32904a0ad649de41b32299dd5a8bf50c35e5dbbd474c258946881b49e335e01c72ab6654741e657bcc5109fbda7297999cb4b272302739ee07cff0d1c9ef454a5dc5403cb7c170337336f94d56f6ccf563af44bcc8382de301720076a7ac1edc1ad95dba129760d7e9cd4df609bb8f7b389e7ab1e85e1b54fa5e3f2163afd7e24c5e5eb61a9e8e79dccefaed30f5fae83bdbd8a1673b36ce894891720b476652a26401e7890af9199bfc74fd0aeb6622fed8e488b8151a6c51a11bd808702f945ae9f9a016bdd94f45279d4765bb376df7fed5506b52b74c867c087c3386f74b130cacd7bcb4e832b697b50c796fa8fe4881c3f994820799417885af464f4f1a85989e5e3b54c3bd878a7ab054bd08c1ad850c96f3c0105380323908fdc94771609b10e489a8445f751f75a9b617cb1aec4460be2b53920e52f7d85096065fecbc6f19daae458f657dfc894758f3d9a6519fb4276e16b38aaeebcefba89a7686546901c57a29b95266d7ab87155a854ba9b269ac94a279e9c4bb38e6a4928cf6fdc3984b4363cc44e0e3cd7a7edf08388b9ecd5a04c55ac26b98f98cda79cd3f402caba28360899cca559be68d7a825022f9010e65f9a6564fcd0ec1cc4c0f4029086c90288fa83656cf2ca2cf30d5c423342cd15b9197905e9ab30a9d147aa2599abae71070e40058f0787ae39f39d6e5c1a8184b2cb3b1e6e45c40c61d46af8490cc9af6422a439f16adfe93a3cbe88f63df5eb8fcde9ea111ca96ea7be416d030500f2c7cf967dcaa4d3ed76a613b2bcc034ed3aa84f35556e7a65a8798825b576236798318034204f041dc59a9f9c407d34c2ca2d77470700ed6442cea5282ee99ceb71c6bf074423dc0785a39b89b062f89231af868812777536d0b144212d5b4b6fc7b87ea04fcd6f94438704f985b746a13dd519c57260f87150db4dc483b664944b9e805197df797be099940fed468b1e214ae99a68e99c6b5cf3d30fdbb1b3cc009ea58958eb4c4a7b7bad6b34fcea2a87eeafda01dca5223458affe54422ca70ef541ca8437171943d5bcdb70eca8de02b93098653160cae52f4d72fe5a6a7ad51c6465388f0362e6e72a3c96b9cad2248e8d9a2cdf89747444c7e51e2f89eb19daac76da37180e4133e0dcab15fc72c7bb90c1024d03eb2e630fc6d2537a101961b388e32ca5019a626b5a23914b06e7822905a5e3c21369b69fa63fc9f43cf7ecb6749c668c7a940b82f88c71802ce6e3c68b5c10c9249ba356bb3bb0539b581a8bd6ca141e5dc2b5932251746c1fc1c3da475581f3e3717f5d905f5ff3c83f1523f57cb750dabae0404beb55aba1105b995268906131835718cd43240bae9aecb30c794a8b186436cf752179b1de65b2a87917147612e3c95288a3e816238b3fccccae1da8041b4f784ddcb3d37ef3f019ef997bd8a4e172d397106a5adebb166c5f565016bb1c5f7f1a0cbb5bb22cd642fa4390ea640c04a968fb5a49ef929df1cafbb69c124ea58b31bf066be388d7b6d4c52409581da785d6447fd72b54949e4c1ad08fa64b520f2b8df83bbfa417952b87fbf802652a65df0d388ea32dfcab3227f645b6b28b66087819b4339ad171a48760e2bf90f1a01184389e23e7df583eab7c2dce385bb7a4d60ae5573966f916bb84aaec6cafe710f839b103532f9a7a556bfa116a05480386ddefa2858ac7ebdb58664a0e633a53e1685d71d5aad11f5eeb3d9ae30ab120fb88f96f383bb86917b72e02768f071ffca7e3fc2ac79846a36f6bd72c58aff03f9e87fdbeb65c8ce5976255227778cfa40ce0eee943b66a382e172abc814ebbcb0bf49efa75f87ef8621d227224b7be51b73a928c23a8937570e1a046bf1fd4c6993a9c14e552915953776265f78882ac8302f70900f9d612d2e7b7dad88cdedcdce7e615baf89ef249d6d535f92a2ec90a7435785f4d0c7a75812b875ec22e638f4af1705cc362ecf52c67900c888d068dc6f72a161013c30dcaa49cbf25c56e8937aa4897451061dcc7579ca99a25348e76ff121983444553419e820a3d1a41744f4227dca054c454a6497e0762155e2a06d17acb44ea55f54e4d6d2e1631a63d9494cf69c38ba66deb9d95c0ee01950dff8e6a5d1c46fececc7027997b8ec626f8405ae99ac20600898fd7fc422e6cd3f774b4009f371ce38e1f089ec93ec0a48d6bfb90ee79c3bcff321eeea33a44022ab910458984b75c5089cec0d8beec20c4c39311b991154fac7c0461b7d667488dc9f0f5497cf66166782e2dfb69a71031fd0251918e4c1243aa4dfb9fc2b6123b4f02f436c0f8737a6663af2b99fbd22c6af539ca9389bfb4e55da2c9ad42521ca6978f71991f53fc8cb227e228a62f3e22d7c3eca8ecee260e55084ad3ace0d6379d5eaf82bf6d3b69c4e74fade4210114d95b41a4c004858119a9a9b178d5a0cc97386bfed0dc6edbac5281c2842951a0c4d7da10207523973c676b8e6887a087c1c2f156970cc6db9cbd3c8ec1c9c1bec41fcbfdb4e8929b67e6c1939bb990ee2cfec3197b4ce824d25a7b4c765945e85ff2b096037adc4dd763b87850fb39aa98f1d395c9353ae3b5b53842a7056c011fb690e856401fc6f67b1c0a5414fc229284e74c230ced260d4fafc5fad153a5154d35c3bf9c559239ce18ca9810a1254a0a6c2960a53d838431447739261dc3b055a0b115914af263ac9fce2b512884620451156c40cd84b7bb2d449aad243a8aea3859412476ddbb5eb73028bfc6b2a942839e363262f94795aa45f6579680997d6acd7a8c8daeace08362fcd420f2f8a96801c002361b942493cc5ae79884eddee989af9832d55319ec033956660c5f11b9fbff9f1410a18c0e9a38e9261bdd13bc392a18a3df97aa33e7f59c125f88d78c86058f3c7e9a9d3b37bdb1e05d90375de0090b418de3ca761560f079a6ddf328e72cd89dcb63c9dd0357ec736782696241e2095aa6107a8bf7bbcde26d5d14533f28ddad7fe1b3f0c181c0e6d55204f6ce79cfc557cfb2d54cd2f33adf17a837243b51705bca7ad8cc452e5340732e678274f9eb532bfea122cfd2959b2499ad25a18db7dc387e976f8b5510821cfff778be799b80505cf38ab1ba6b01561ff81ef17f7735a1ed6a319a7f571daf92afb7490fc17064ccb6a8453ae6e66a2afdcded0c7359461278c49d342df84c43a0607b7327853db675e4c9ca95c52ae013461ca0763d157ac992e219a6683a547c32374631b53138676f50ae0d30588fb25de808e1d9efd5fb9485192428477c2f64fcaa862761c7f5505cc650910224400e2a2db13fbea0b0cc358d163c6d4a8e6296e4c9554078a55f23e60de9bea12ac199cd19b97babe61b2a3b853860b4441ae8dcd85d59695393816c64bfc074e945452080e15bcf6776cbb6b251c13be63932702468679b2bd7e1693a1d910e1b62be91e177a8dec8e4d7642e88e513e3a871ca502854ab0fb7fb8a099d0e0dfd73064012051af337abc78bbbc6fa2dc7169ebfde2d40167048d78f12cb5e7c130a2137a0b26922553f43aefdcdcec55bdbee0fa752a4787ea9d1c75d1bcd9d28135515a0ecfa9735901a0eb89f915d036e21263f90606f65e62f6e5c8237db14d570612611b3ceb4d026ac13f473e5706cd7975c95d49734253757f9c1e6aca9261cd2b95722098c5f7c74daa6c926ea4867ab54e4605a15bbf7ab94348d9b45c31a1fbdc7f420cccce25abb4249cf413008a01e53296318414dfac822cdc0bfc4298273f65cda3fcd61236e255dedd879fbf20b8c4843e27ef413c7a1ce2c714c88358091abccd7fed337a7d4f725ffbf7856ea4694113d605b1ff7b2bdbaf60f7abf7d43eebb1e82f4b6b7f3f8fc23ae27b09f78a7a3b324c7790431c800293e08fed2fa49aafe8f5670d4e6bff4cf6e7924993d6e18ad87699da99a2f2ca9f3c7b44b3b61dcfa5f041f7a6df94131b8e5c04069678541c87872daeb2943902afa6ff4798e45a75659568a02471afde44f8e116fff4eb7c16053a7aa370bcddbf5229c231ef8f2e642c1a88f90e0a309d24a3ab28d3adc176665e522cf5bda0285baca5bf2a0fb4c0fefff3b8ea1522c9ef29342ea57eb4f16d69bcf948d66247ed2f2cc4e567476f736d170c98398ac7c0d158f294ff33ec8d2794ec1929c57ee6d7f46da8e40337a2b380717227ed82eb13a14d129069e98cdd65bc34472fe1a52dfa7e466e559705c53d73d97239e38bbd600aaab107ced36fe7d8ef2387a05d8548a48a684fdf02b984022aeb28e8c5eccd1941ae19d2cb12a31de296b4fd1409266c43118848962e9e59a41054a52bf2f20b09eb4e77cca72df70cdc4d712d5c0313ddf9ec8df2d901145341b3ccfcbed77288ce3e4afc677ad8681c746eca02e7f7538d0108b68a922a21d6a8c2cfd67d8849ff5779de1c00588133309d4158d1738e508a1a2ba3351a46fd3f86339d66b1244bc2788de3fc60144d03c08b0ef08914088f9ce947d2b230ffb9ce176a4bd45b0c8b100e17ec7829eb1ce10b1a514b5e33d4f40792f0a300a4c3ba7a912db8e19cb72ee02ae8ddafc2caf116df905640d0cb65cd0c61d38e0a9741d3dc2b7677e8ec051d329183c954ff9c0c2d8771182d5550a742ba8147ec6571bde519c2f57f10aeb1c8900f8324f017f3b1dad92fb0d19a76de75c0c7cb67d079c8bc0bd7355fa25752d5d00ab00f9ab332aa23aaf8b5cfa4d2723b2020c9e494208c61a76e18c9a583172d6ddf544a327c931e8b908830e46f184998fc0d34ebe767e4f5d06f99804759c7e2b76a8baa302d6950f90194b0cc694a0d351374cd028ba6b0414377a850ca2f209d13cb7daf43d14f606a0e549ce14189023fc7b45e2b095a3f45930a72296eced3e2da24bf237959c0b966bb88a3a9818932b1cd41e8ee002df515ec94901902b2e67545dae53e08810b99dade7b579b847a6570211687a62a56dcb324bb442c4fc22f9d85e54b3f939937daba84c86fcc36c83ca87de70fc7f4afed335e8f9aae027e854e3a022a053c3b22be3617240f1355be9af3f4c8e9481005cbef07bf1380ddc012412c31b221275b56c3ad52cb2f7a9b340526e6128416025d4fe497f2d502070653d36f924420c0844b5560a8728e4a3be8b1ce67a1541c00e97aef9c808bd77af5aa280690d98f5ec218bf5c9d135d85069405b82c221d834b8285402f481b7d51ecdca91c4c2b27c12f486d382e0c14ec23c2bdf33ea8ff693af551fbe55e46bd84f335a68646bd092f914da4954c57201d7e812f4974a553f043ace9c6ce2c609cb4d90dc1ceacd5d610cf4723024538cfedc1bb22c9663ac541f89f97b37d2e1b4d8073ada1596e684b94e74c2c2f0087251f1eaa6789ece436b2940d3285f297cb7482aa9b8c47118a0b1d2031fc311689828b64855bc17306529e4b0a3326438b42698dd4e2e0c2f624aa510a2363583cbac47fbf9974925d81af9534d0ccb41e2b445eb19569d703cd8ace661139fa344a5e097290c233114370831d4710676f7e93889ed533454928333655b06ac5ae67f301f2b7944d769892ed76374a1f3861c8624b2a302df1a2f1a60d41e61379ab3681d8eee152c251e1b7b0806aab2f287338b752918cb989d864e9acaa92944f5194f8eba19a18a025157d2c3988163baf8b7ca9060edc2227ade8e05348d86d18f9eb633e4f06ee346e4c2912ed1c763a75fd1baf32cffd0c856ff9b44f1470136c0a8ae0b88175a7e7f993d8469cc8ac3f643aa7db36992cc97011be0a42ff1df8aa7cd82e260e05fdd67304644335b386c7825858a438c51b63f877af005f7d95202b54a2130a1f652ff7addfb798daf4415572376fdd910cd099066e8d2fe251a907e41bb950e960fe5116f4a4fc6037273250569b41ac07002ecf3b4b63f8b853f9693badd2e8bff695266095f4b2f38ee4d60428a45e7586d9d47c01c12c5e18d1ca9afedca6f03384567ddc730fb8fed27516eb7c54135cc7447af831f0c37727997ab2b01fdbc5e12c112aa987e95cf5aaaf44be14bf4882f49cd84894d555655d17aff21f077b2cca1ff0922afbcbf4781145cfb87df585e6af1955db04843a763fa735d33b3df836585c5894e5125d500673d5e4d0bdf6708d8d28c80298a9bb47c7430ae8ec52285a4c14371b3f8ac1e21306b34bcb0c1e3aa7a8dc8955cd9cac32a0f69d0fa0aab30abcbeb2c21d655ccdbae1c2040faedfc7a5ef29cd5d9227908335ebd257ae8c410b1916b9a5bf0e72902c2bcf701a18c1fe13695a8435b0d0129f3db883f428acea8b7f274a629865f1849d4c263a1536421875ffacf058f0128f0d012fd60f2b3fa6b71fbb96f3e1e33faa09b1d20b0919018e91a6bd9595e9c16d4fa6665c4d0adbd77a59cbbbd587029a4f306c887277a7a27748a6f289edfa16c7e4042fc81355b85a994cc6784f5fde054f188b9775836594abd82c1309a66db4388ad49f60794d8c8e1fe279f8965312a1e23c2956427bd6e289e55ec728f66c36526d40aed7c44e5352fa3e2b5d27bdc1f7b3bfc4f095d002bc8e7b2df2032daea9bd3e0bed2733e7ba13c6d8b45c492becabbef0e8c62c257480faaf6aefede888ce1546046f04ce7e06936cbda44c5f33c71c07b53fb3ac7a72c3770838471aace69f7ea454d2980fb5462173aaade7964b888606741270f92bc18950284d013b692bad63ec3726d8f6596788a032eee6eba08eecc836202b06ca43fcdb062e65f8ec91f7400963bbb8094de4b4e1042e040e81130a3ff037e1b7e947b0c3e8b7ab5b539f8570ec2cf08971a1bf0d4f3e8311b5c625b7d032fe2e0603ac1a4824432b4c7bb5afa190afa30c36f168d196fc89015aff8229fd4b299206e8183897229e95ba06324edc1a9ac81aad786d5c9d38c87c48fc6159934a8532489f06e1aa314a057d91c0f7218861ec241dfccce63d60bfa624652795229fff21cf5ca61ce05ea0ad6a1763173675377d1f2427564e397683428fbcc4b97495735d075f84b289b73389a1fece959f9d9b4932f4d33a0907202c650ccf65327d24cc24922d0d675fb59621e2bcd2eb31ed1ec5fddc96593d4f936ad42731a50f164e404b93920283bf9fa43f8be5878d18e07cb8813914508532625f71d02a615bf47018e3b3efed72d17e0c974d33df18ae70e3a4a743ac3c955d1c366bc94d789c90a8b3771969210c7f0c4b9b12f9fedca5d2b71b6c04e7024a0a5e3d0d3c7d30cf568a659cc17309b223399b39ee5efd0cdec3a34b0735df8d7e0bafa40a8aefd742b3e0157fc89d113af5fcb0cd136a9cfb82acce406054aeda30b83ade59cb6771c366c86b5d9b016a74f220a967b83254db3e24d3e1f5d5e23b21354420dfca6bd8454575c6b0e528744f44ff8e1b9679b084d083b9ebeff09df273a476332dfa172cb98c79ecbf3ae4a722c25be71a3b206d848f56b02fd04bd32ff959e6931bf6cba042f9fde027d832632cc40d4320166f9644296d2dcc845491ed780cb609c0432408d08dddc70fa2d52884df47a55a8a3e706a24ac6ceb375a894636e1ac121040561ef486ffeebed3c5336d123dc05a5cf221b9e895f6d3ffb24b3674039d50fec114d3648d6dcf4fbc0b503658672d675c4ac64dac4db4615d011e82bcabd9573fe9e2af224103f99c4e6e5acd2196f1eb2260fb4aa61c298f99c20c4ac1c9e70a9a9e28db441c3adf2f2b3a00ca3f1398b5a112126fc3f0e2d11d40db36d0aa47d59cf8ece36df8daa854509e546295bebf92465f32a7edae6af4218b49781034329c22ce9e87258216b4a901b823a227e46f2938df336d3fe03fdbff4efc7b8ff8142b4bb60592abe508675d04081e5eee99468971fd49989eddf4648d3948266b08133b8c22ce3523757821a05f254cf5691848b1532655e85fece5eb186e87fea61936fa63aa047b5cb77c08aeeb2991c26619139b951ba4c2ed487703a6123c9c16d7b6cd3a17ad108fbb7c79fc3706f654be77a6cd8a5213d3976b1a4527144e788b712f37cbfd5f4ea5aa5fcc0f91b98d74ddcacb83fb33e039e1b88811a2138743ffc0dc5eae46c688f48c0be92a7094f92c2d4bae0c1dd95d40ffa113f27b8fd1808178be0c3bf77b677df2a17c9443fb6023c9e43c48614fd7d09a904e7a064125f8b7671fbb7529169138feed4bf3e6c215947efbf44fdb5eb1e3a5700fc8bcd1cf2a393ed438e68b607a1061e6cbd46a0b4f30fb4a492e786c5cc6d4f80542d64d0d271308ba072deaf7b1ff1c5200a2bb24c68aa2a5cd07052ee5760e0ede484e0a9c482cc74ab564654787e1698bef1386fe446106af25cf2fd950a78f4b521d547c83b6e4b6ef652444af342e90bb5761ea0dbf6b8c315996ed3d3a1a9d1e5f2b75ab18d0bcc87ee639299a248406d9deefecbb6883fe81ad12f20d80fc9b3c8962cd84e5da215b96bd457d5146aeb36d08185b2ee021fef7fdd43d0555c0fd16e7b66d1f534ae0b87f6299fc3b8e70236f39c91601564a5c2b3abce248ecd5450096e65df8b69de8b5cd963127f207f0df538f990bf29ff0c2930ab8b378b86d2619b150ab4dde82469e40e9e6e503182882b03442f92cf7449fc2c4b2f7ab91cf5ec57b8b0ebc18b27ad9125da57b6c139edbe7495defc73d68e71019b2b808a1ecb89fde58ed9dfce8b17e072170983e3dc3551149108582b95b2e4bd9552faff0bc7fd13dc0130f62a967761928dd62c9cae970ee447842a15baac31a280b443390bfefb7d4115e8d1c6458e1920485985635b75bf84b6af5226dd9b36c94bcd1a5f3ed99fc3e5adc6f3c4a592d2eaecce02d0e5ae97e6c4c7e101d4138e526fe3f090123c6df8596505b4d28c1be9e0c3f83dd4ac07946617eb553f7688f6724c8d8d34281a8c094740170280cec80b61b07eadb2c63427690ce73233f18593537e07266a71523688e6fdd36943d015c1bde058ebf5fcd12ea00e6b41565d062722941ca346b5949078421cf1f32fd38ce1be52fd65f2e9862bc5c57b45f33581eb72d387f0c212d68f85baf7e42058e41966429b0189da4eb4e58c1104e59d34eb0678a1385c38289210ac86f77cf8bef670b9ed04414875a29c27b79bc97e7febc40c39cc8e201ca56b25cd8c96f1a37ff688fd5d55cf5232d216283169b51cb41c631ca323056f2e8f74d5178a83b3bf43cfbbbb40f2680f338a89b294d4b611b0f275cc60e99bfcf270cb761c1c2a6773503a3c0e03b2a858d0cb5f81b032012bb0e15add080cecf19bedadc4d0c7e9d81fa75b97e7ca5e1c9eec88bcd10afc0d8e48f83aee498633bb260d731e0f900a9b0ae3f0d6e57346b8d3d8019bf079b4f909b6dce5718052416de8b61e9ef317ecf2faec5a64c0ce4bd5aa72b79360ff9b58d4a5f3e0648000d90a947933b0fe71d81ebaa96cd4c54c5d42b941e73423efb1f6626767f0e6dde52cd8796b0db31cedaad75b4ff72b83e0e9ece164c4f4dac170cca65e42db8b7ead0fa6e2f0b2d9576c79d1d73aa4b7ce4b954958dea381a65d4287bb1e02b7b88b33b2f1872d92ec6bf79fbb726aa436434fc7f2e79f0ff8bf243e19c08f680a5012c3c3d0501abb46026c420e485166fff457412a075086266b854aeaf5ac3a300ff5501bfd7ae96ebe56a5df5b7abb5fe2d9bf7ffc0005b6aae9dca8380a66c8c14e884f9f3338346ef1daad62042d09cd774b43016b0339aa208c5d6c2f7373797202ffe680edce71d684030e5841edd57cacb0cd596c63515a1b992de113fd2c8330840915d3409f608c8d43601f09dddeda6c504352e4d8ed932d8b5076a557fa2d87914ccbb0fed61c1f4d9950be0900b2212a82abcc4d9f4466d1f5469939c1e35dcd2d124623e2e1aa2dbe90ce09b958edc4e60d7e7502ed16a8a1d08dfcef84eed8a93d6940441ad41e493ec09abce869a8de0a778fd8df55cd1d9d7f053a72314e786088abdf968a3b1d1427e52427c2313182f51b5556466960017468a35ed2594280ad8493a5d3503ee4c479fd4c8db0141fb6b77331b46fb228f94e213ad372c09ebfc8bbcc79eb074162b4694b7bfb8444a4938a80747d2c15879e28e3cf44a8ac2ef8c5d9f93e9cb42f81cf88035dab96b6c078bceb4fb9cf293dc964b0cc9b722a84f3423bd4fbc57ad1f68a8166e86da595fb3b7a4dd87190dc65756e0f7750b68e3f605def2821619fb4fada9cfd1c41343dd4f5424a1911ca709ca745962a484d981db73a23a85c046a3eb73cb48b085d3286738e689437cd4976cbd3e2c5a4a4e385b3aadd09b08859bcf5506164db33d414caee679c65069543f1db8b242dc424af5a45bcb5ef9b939ce94e4dfe56ca25702d7912c38dc44d94670d90109ab685ccd59988bb7e932ffebc882b7dab274065578c041591d481501797b4039da17684a7242622f4390544b05023d3902949d348a44d9ff421d04a55395cb4248c7cb0f13b697f1423be1342d31fa9b85aab70c16bc62eae5660256d2472dc908768ccb7ff02cc84ffc6059752499711338e41d37f8751d84eec47a51c7daaa273e19c523d3d97ee5f71571def6f7268f24afaae71eafc84986eb8a0bdd50fa553c815939a4b56380b22b650fbb989b525d2862c5d420c83509b5c76b1a9a2105b45c2182331ce72e8f1804efee93b1929338f" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a89834aba90aa88f605981578667395d730a408f3d112231997200c28865ec37a4a1f044c8817949cfc8a4fd9028832fc406f1bb919789f2099629c2c8d6bd128de4f51c02a73c49dbf17883ef90a9dbc0350aa67e024a893e555b4f989780a45320af4858421346445f66aa86e4954c0bce0debc896f21bc960e4fbd4eebed93e943a40387210ecda652c36b6da73570500adae1a2a03284dc47f1e370570a07007d8c5a64089e81e2c4d45eb6ca01530884070d46f4605ffe139457d5155c8e9ce8f201715ce7b9d18755304ddb6871551498985ab400cdc4349c6b5ed10b76b425e70376e39a6167622eb5d4b7cbd932aeeaca53b3e28ea06c44780b01684e66eed712559543cea677f1d9807e2ad0312c42e43d399302a0c4c506da7cba37000c3ecd10b58922889e5c8d31a9882ab8ae4b69d3d3d3abc6450881e9b79d9e2ac51085f61ddcfde73e57c1784a6a766ee2bc434d4973a9667c1c8e00eba0c544a36c593a764f519de83251ec48fb45a63e00ad4aaf01d1a295eda0eb95b27fc364c33e3482f4e5256cd622242840d0630194316033219c3051258f548a23c08d205a7b15199ba8d6507565d06a1f20db367183d56d9dcfd2e12c36bed83d239ddbda9efe8507451542942033282bce486f002a215c2ef599a6619247888a81782b005acb3d550988c1f22c6b186038bbfe1c583d165588e00c7f0f7e462a778e6a8126be71ee0dae7b5cfc6145aa6e65c8c136d34f7b45252652995898150cbf177f47ca8d16b583ce5b0e809567c08b99c06095c879b4d945b13c8a62429966347946b606dd78803a3c8113a431761dc868ba2e2541c66b39f30ee21b0ee3fba5eee922d56f49ea60c25a1094381908a69b351e325357d9b4b9e2d040d8be7795891ad46a334a057b0aebdb2e5a9cd760b02c774f60df240e05ed3708840b1663cb435adf9929c138b9fd15461d86328d5895d8778ada79d9b567386a6302b67b4b99a37f4362dea36fa2fa1f1ee2f4f8293b131c57595468aa5ba62a03dfadc9784c8190559887603c4b50b7487e795285705f07f90d8a9ca50e4abf0905d7487f04549132cb3a62ac2e490d6d5bfde20568290b693681218426fa0458eb356b198b41bc60505afdf7219ce5959124e4d1a56ba63345f91ab39afd970997895689c8397c023b1c8b774f92f0e5940c764abe3bd609a0d50a98dfe291e1677e6232896ccdea3d047f35c558daa130ff53cc66aa03ca5687a2006cb7c8c3f", + "slh-dsa-sha2-128s" : "0x3345627b00d77c13ce3ebdd12964c45d0bec4dcaea60b64fbf6cbe7b897b8136" + }, + "pop" : { + "falcon-512" : "0x398665ef3725ef330b6f53d5f76d657e97169314a4005b2a0d60feba61548a5feeb57b8c985312bebf8466f329bb42c76e604d7555168cd6f373a22b3827f043a3224851071f9ecb55386243392eba25baf906f8c035eeac71c9c523b5968d7df4cc7dfa52e4919bce0f474a9cb04fbc698260e4d37114ecf919434117cc9a0e7befce3559125d60c2a0e93a9dcef72ee54c47d0dd657b45dbfff4b586cf5b9e4c33ec8dc53632040766a2df13ccbbe6a649df9649bab629a6a5a3cd33b53fe7e20ae515aad9db894e75ffc44f786ea2b054c75e560a31e46c29308451fad294989d91c0883125430fc439d648d49e9457b546884e2a471dcd7df66ac42565c017fcee3190a09cdcadba95e836bd292f83f9b2c4ea08fdf6ba44d856a6c375bde9083bbf2b6d89e32a19427a4613a38351f448dbad1da609a551259582d8c843a0c89d5b3b9f85b24fc469896e8b9683abee444ef7288c98561f4657f236e9142de5e7a98960ef4d35576fcae78e9e6ef5dd28a19435c79b9f37b71dd85c070ead1d4c7695b763bbe8d689c566e22a224902a24418548b227be8736d8cb98c2cc20a3042759e43935409ece39c96f7eb90516b73efe4732597e7c088e9185eacd08f094b4311c2c8f4cb770b7a7b5fd526ac44111b72a50a0b7fa36c2c9bab7d4ec39286bf509bcb08ea221fab495fd050b2a83b1bdc308405088144f649162aaef2d71f94db507628ad673152c29b05fb1aa3d1a68a867b6472347a54845549f7cbec83328f8c71f1d862195edd15164d7652078d8d4f5c0d2419948d13d204d3d4f998f336621b7479c526ccb629abf3b0e3a0eee6ab363ee7936117e4ad8445a124ec2215298f2b38ac95c5d3402f3c9c1f0d34b4e0d7dc9c95dde6f18c1b59676615cec25c340522153a75767c560e6f7d80", + "slh-dsa-sha2-128s" : "0x6e16547734391aae71d29745c3ac8cdfbc98f07ad9a8470650f0a0085883ff970643e21b67d6ffe83653a99f2198f821cc080dd7f0cd1a612ab5c6f18d9903ecce34a5cdf069ac66483448219c06b131b377900b999f9aecf637713feedd8c7d4beb03ee11bb297bf4a97959ac5c85c0c83b48147fa9a3be7543ce1050394e83ed745f9ec0c555328d3786da46acac3074c0611667ce5bb4613436dac210c2e9cb186e9540d7d43ceed7f278363811083368d50f6d258a46a32c8df5d62c42c4f6f34c627076169428e646774dd6dfe6924e2240e720fd4d53dee8f880c782862de1ff96fc5204a17625ca008781cb120f74caa37a36dd20136567c05e8e7723a2e21569f065d4fc631811ed0b2d9bb67d2c82b755d14cdeaccb797f207427d572419360af1c443fa68064a653b1e21a2b83fbd93e38c7cfa5046eda9b86c68f3855c61cee9f799ba02e611a1b7df6a15fdc4b4b09e51b3b5e1101d0dc90a213cfa0522e93808fc4ab3d8cef74d279b98069988f6b8c6b4c8d3d62fe2a5a62f1a7fb0fbac7fb36529dd85416a330f35a7e4e161757a83282ad50ddf92c6096cc1fe071952dae4540e0c9d0507d4b61ad34751ce17fdf2e99c3e54cb523d3ffe2cbe8a5c58b84bdde119175e9599df6b2666a7721e246305457cdb5ff5ab4ec9d04bca439d59989253bbfd9a91cb10a9fac3134d85ef28f1e4d4f884aa195216a4811be7617c0d6004c7833088851fb7758aa5aac838ddc5e3a51d122fbb03b0b5d0a3593b2b97eb69c2266eb236bcf3134414536ff36895bb081e9fdd2e5a9f9ba71310af0fa126eddac07ca67020ddd4caafc9b102afd4d0bcae2a957bd65d0a15762b98f4dc7b548cfb463ae10ec3da3b5a4e9976b8f05405904aff74d3d536f9a321b41e0c1d5e4fdbbf61b42d868602e112b53552b8199a6618723e5638273822fe0afc5e6fe068d117bd5af3a1c1e8bb4ea9ae639674b51a523d19f950629a76e9a52c673dd153915863d0ad166d357f8d23b85010aa0b8263de0f253a0ec29b0973af15e4419d965164b5fc6a0e782716d86bfeedfabf4d9705e7093373819a814a45b245cdd13a667b31f1724b5cbffe3b5ed3087118715a0ee68af333bf26f1ac1ff13eaad6a5edd5e760e89518e24a982af4f44386c6d92db7a2229bc9c4df23274cb0f10d348f2373a79f55ee05e3dcb256df464c5f3e54ab790f1fdc585cbacd93c775e50bd02f451b69972728bdce813b46742ebe9e45d45c8bc23d4b298b5956e03b48c0a11c602ccdebace8f92e2521cb346f291717cc1881b0fe3a2e23d4ac694b7f1cd296b32b7f2f139877a129079a524d7e3c4f9c36b5836346a6944c49967796d8564ac8d2ed5e804a14c655ef235fcac5a8cf0372f7df952af0de7e4319089b54ea3f718b539a9bca631ea0bcc9259c64626d252245d541d90179364072b081d70da0a7e561ba2902e8ce864a398dccf55a4f583df7b6f6ad6738813eef6815f8f67936b7b842ed7dcc48808156d236d70a9ecba41994bc0296c3a236f2772c747db6ad62723c4457bf8a2e526f306d8012e04adcff0015a88167d199029ecbd333a6ea35f5b9dc2eaf11a14fd09b35bb2842a4e610da4afef15f332d5d1e82c7b0b452b7564eb45193af060595975d496ee59151e3c0f1545ef2694eeb1a2e38d4e008c0943c8e7f181a258c35e5068df39376aad6ef356e0010303d9a0dbcab91dc708b8e9482f50aa18f88f45864d539dc0818148149aa259eeb270a474b0a117de91b6834672a5cb4b5fb7c2ea0ea7e3bc9884163887986aff1cd89c49a6ca417dff067da528dd33dcd1f7db96ed50820bd72dc41984ed5c767478b79d8f61cb7b417893a3a4451129525a915b1e999086e6db74ef627534015206953ed3a5e2bbc5c629b00f3f3b22a37519426c20b2a10c60087ea32fe606d81c6457a02bdcadd8d56360dbd278ced2d4cb9af64af4ec77989475882f3c1c0b9f9e0b6528b9834e34024dbbd64dc19fab35f3bfda66c086f4c0ff06a29e9fe881340d1c0719ef568e3f8f683282b963005e93620c01de327adc0af4ea7f968fa987cb8f38350ebbb3b91192ebb009f9fa85657e06cca6f71edd3d0dbcfafcc11b908cfc38a08ba288910fcb14f97746ac20ff6e6c43eb099d09a717ac03f5e0600f0bb611d6972626999f5661e048476aa999c83e3f476e77e7ae09c6fb7c343a196e1846ef061be75fd8b662932fdd2c7d8784c6e498d4d828c910eca00ac199957fb44d572d4bd2c599fa6c7c2a166d96aaf4959c561d1e9dacf502f76b050c836194fe1897d54a017944a973f49b2ea59b66c24d18f13048e093e83af249854da9fd6b4974b4db3e8e65684b3b6a0a11d942ad5bb667460dd98ab54319cc2ab704a7a57c1583b8289477f5557d2da684aecf66eec3a54da742e2d24a6935ffc4f439d4a8abc21921087df93da2bbdc3a0f61729b397faf12bfe59c85a6af75c9f4ce4af19239840b57c3a4ead6096f2910da23bdfd02ee4f3676897f475d625d0e9a9cc930116bb2327232dacbd0ff32cfaa40aed89024b9ccfacdb76bae77a87b569582442ad2230c8fb3e0dca3937dea5685c46aff9ae6e16438e0e9ea8ba299936a42d68967926edeb3ef955d95c5a0b74a13edd280bdcb462dfc2d82e7ec89377edf6760450e1ebc61f035fff661173de5fc80dc66b8e3d38c67f4ef8c25c2c5626dd2875e10d870f12b3f36f749eaa0c82e94b2f30686cda1da606e0c815a35fe6fd671915483e1b2b48fab000ae560dca0119be7a0a3dfb347ef95c7fcd9a78e76911cb5bd438a7e96562e437e32eb399782a9498ac992716bd9f3d7d4e35a1f1821f9008f547d171ce2b69b007b5e377a3244092396acf958932616bc5cb95059762f48468a94f9f897736b11bec2ce0af9ebaed9add43bf5c262106a96083ad967b7fcb127012b1b37970a6be14cd9088f6ea927a66a76c35e1320452848bf7496fadbc613b2e122e55634f0491397ee5d211d780971998244f303802d7630837d05f3dcc588d8ca32e02f16d2297aa577576ea5c1b90d6d330a18444dba772d7aa94d0bf2000d598a069d03e7873f114cab9174af2f28e257ef2c0e44925da7ccb593d6183fdd37a1abfd7107ce02c655e7966286b72d65394f312d72505803bd53621a08100a041210cb912d5aef260b84e39faa7b5d8a539b6392b32ce6287e1a619804677b3f69ac46fbec010e726f3eb3458433e68e6417ee375dc70c2b2ee16b2cd9b228d6a46f92ba66a8c5b08cbd284dd759478c9f690c5e1e16d6332b5a0b9aeee43ac8ce3eb0c3eb6940b61a65bf7c3348d8dd15302b8b7bf6b6257e074d0af3eda088f7c22e6778c8cfef1f15d9f86c76a4b05c9afa228bc4f116ee8adb959b4ed9afb2307f2252b10baa436795028629d05277ea2722ffc14d9a5fd56e6cd6f0ba218675df025e50586e46429f1c6028e305d22b5479dd045d98a93fa30de08bec1576ea692bfe6b657b4be65578fa2dfcc197212643ec9a1a8de5971c07023abd8767c528025281f9854145e3d9c47e01bd37dc3a35b76639e4716465b862b705ebaf674a64313641de333828d75c16d4f6ebc4193fc2c92e5a3373fc79dcdd8505a8ea7ef8be6ffcecbb2e80017b6fe401e49193ab31e67a56e8163cce767abc393608e7485f41983ce27b9b1de1a0281f237d685d401641f860f8f11d10805fbf0c7125deaf583458787c6f2850b0cc8c62053e069df241192c6af9018158c27e30574a9025af5af73bd956bc008efe84b31fd9a49c110b803947fc939faeb773cd3c33d1b6a96fca0c079f2eeb4e3ff768bbe1cbada9ad22c2d97928389e5f7145a346931e4138d66319f0154cdf3b28cc9bc04139f92e421efb522d0639f02935e228d6eacf3ccf0356b32c163dff6b597d51b64a13c8c93f60f150f445271489db644665ed1208c025b35dcebe82e73034561704c602d1b24e04c08e4ca8fab21c5136b6d4b7af05cf8ed62961b4bc1c2883154116a0ca8933e36042eb98d851be695d8c4de2c2530372c606c93bab254d6b00e59fdaaec98c56415da26ea8449061e12bd22743a1a0e0e1e117a4a9448d75b3a9dfe6f213460ca639632d404af60c764d91405391137ab9ec4e8519d11e3dcae18dfad0404ce14ea4dd36dc3ee24b7a15709410818ed0a42091a01ed9b7f39f7c87d60d2b3dcbba880001a519fb7de152ec06c73d35c08b3e375b9186752931b6108a4b84189fe07a7165e18357a94962dc2761f4eff0df829427646d2210b55915be710e2a1bb413df2055e7aa0b0b9a4067d35881f22c08e61b587a59ef82ed4df267afbcd4c62f22435167f2d7a4a939eaf28a6f1903c8c046b2d7719b7134834cd56964ba860e60ebb6fbf4605a11b52aac2d595cf3b6533fe2452b65dfa0fff71aff39124a88e3e02977d6063a8370f98e3cb8c80f35260a8541b9584b1d2bcb315b394d7641ca56539b4ca8bd9e4fbc594b0a3bf1ed678be2f9a18d31d610b55ee175514738717d47e4821150a799ebce655d0c75228c2160aa1936dd42f9923de2ac97d0dc3ea062d41d5beffdd1d69672b73490d6a5e14a655228bc39ffc8f1e862fd39dc950dfe648e0ff87807e543d02c40411ddfc6c1f8eed733750557c9f68d830c83af737c9d8c3ff0d23987a8e09e6293d707940eef4cc838365775046af35dd26acfbb4264a9ab84818a71c5a13d8e615c5675656671e5fc2e91b682e91eca76ebfeb1c09334e962496f583affefc56dc144873c79d4df77da394845fa29ff348007c8ee7f23f2944c121ee3177879e6938eb043a0abb75991b3e789571e3eff009cf089acee93612e97be85e1cd83da9ff5006c14151afa67fddbd9262667f98df52dfded25f28b36fa32ec662cd5a6b0f844030dbe1c87e9e2d435fca1a81045b8de5b8ccc411a839064bfd133731f55ee2ed2ee727d9382d130ced8a48942ea295bcf75fc74e24204b808956bfc171392f18554764aa80e75a539941d90a4afbce1f981f0c35bcfc78330ecf3d416d0683c7682cbca066a10c43273a0f85495cb4916ec0eee9952431a11717cadc95c7626436953fdd5fbfbf9ffe329159d1bd79499807796d75c458c3e3914d1f73f656e9fa273244615710c3eee84553a7ca0250e97a0b8895695bf86408d2f3cb2fbffffd62c8583af5b25b7e2a38e266c9623429f8d80ab8fdda6774df598f966b86ac228639af66cd0e3ee7ad7714b4ecb42876c31eb2608fab0c6ee26e97fca305aceeee80bbe22cb5e9819322aedff05cdb78b1fb3060ef18ce0385ae0fe6803b414aaf0b340eef94a7517e5cc3df4bd273f92120b8ce73281cab96821ddaab08b41b97708dbc7d0edc49cb362759905ce8d42743f83a0d75878596c4c5ca878b7aae6fee6ca7b20f2ee60ea773499a781b958f4353ceb078e5c04b0fef3e0112dd0e3a7ef19e4c63a202fdcb8ea56d15b2fb0deee4198a19c9aa59c1ef2b3de73f291445c248779eb5fc76953137adce5dff20f2198cc9e729be4adf385e0f25f67f68c04b4cc26987c133b987f477d89977621b8313d6d18a5ff6a76c73ed608f5e4adb860e2e8a6cdf237d4871a98f0c448668bb42f29ccad560fc1e571f8abb3c75665a65a212fe9d2c1c48eae9b92bd0a8b7b3d2fef99ffaf7942f5834533a592423d2cd66aa793e85bc8351876946a1e49e13ee374609932ec1af8d4d3f52e00f783368aa2bba882cc91c5fc6eeb657f3682dca8e9d63f319df3bd1cf52c511f600a6c815e4ad59426d9e1f32831dc5d8b0673c7c58e8bc1601d241bcb76c1306ddd5aea847dc903f376bc4d8e73a55f0a5ad331f57bee70533b29e6b8493d9f11ce4f27f9605f693c05487bd43882ebe8a1090a95abcfc186d74a3708ce3351ce7226183a21c08497dcf52727b7de08ad6a970b2c9f314bc7b33995ffc77a9a36ab46c7a05f61315d18b3f2217703be5753b4ee1670eba3af00e90a94664039aac93e89dcaa59d4566bf286f33c740feb763d0a385855f4410052c52abcfc972298ce03ddcb2e65dca251afbbb75c516ba77001dcaad8470f61e3391c1c68b1b7dfac9e50d53d2ad12876ba96419b39d7f94fd88b58ac1c123790bca733b12dadec6922384c6f3b7284334bed918acc06ecac7674428d56496e9d2e8f0175b3c7e384218380b6d59dc93541ac0a7419eb0ced7ca8c12a487fef9ea50280b7f7067a8855e784362e85703f6b848ddcd4bd31293baecda021fcaa7198b27d37900ac192d71de4e5124089bc1c8234fc455fe71171fc44494395b323fe3575ac720af5cff428326cca56ceb0c47e20e1432018a769f467405f5a8b71e15e0bd32f027640212d893678ec6fa16e5c00bf5c0eaf2de0333126ebba6b2306211cbc731a6baa8fc5a64a30141a8bde237c217161e384af3355b4a253d35766a55cc69be97abead95dc2ccdb4d243f64aa6d74330cbb2fc9fffa79fb6b6baf99e5ba9abd7966efc1586e72bd25f6563fbe125abec67f24e3510bcaa34c49499811ad2425c25593ff362c90c5c4fb3c4c45a4c63c46cae8cb1759ff2d82ac2726b60b4f094b6344191f3828c78d369956b7a28f650f63260c2dcdfa66d1df4731866efa6a335281d1cf2ce3635eb4294c8b2e1d4b7ced7b4b58a10d693e9f5a269b9d3451198735697af3a7b4b630b835e3b56e252f71f9aa6dcdfddfae13668bbbe3538d5aca766dbab6655c4aa906e0106640d21c30f1a241f4f5dedd526e8f579848f1b64808afb54bd2737a18d9a0e4a078705847b59a1ca4b2b554d03304daedca50726cd23326a6507393b2dff27921b5d7d322cb9901e0efce9bfd95039bb3bf6a99282f9dfb1e90289e986a657b3aa212cec6e611423eb3e4523f4723302b1b4ea5db2cc3fe3a7f0f3add38eaf228a6f0c02b9237e4078b438c01a0856d64053427af9f84eb04f022fd12986287b48cde73554b014f75a04d01f7d3fc6285347be643ce214b72c911e6c5360909ec590ffec8b4395fe271e97af852edd3801ccf04d3ed6684c66c67278cf331ac7c9b748e2dff377cafc7fc6732352245aabc9f5f25cc29d8f7f211a88a7aac750e488ad0304b7907bb063e29fccb16dd7ca0b78fc3962046a83f9c7138d49df3b765df47829e5f56e5155dba194d5128648b92e4f5c11bdc9232725faea4b87d6d940b9bd7cead9462736c7ef605336c40c9a053a000ac390b6340dc85a6905d35dc83c2d13786d233952ca8777b5981b761376f4ce2c5bec0587c2b28aecf39371b8899d227dd58c054aea7257dcfd838c32d6053362842a0baee7f967b6a4f5e1cac240896a8ec8fabbc4a551a55e674dc8cede1b9cee3874ff9fd3b91db433a12e78904aa3ec84d57a0169f1719297b467c46b278f671f74b8bb3c2c0cc2a093cf93439c1267e3be38b12e9b5881c27ab47bb01397c6470b80174ede3df34ee51ac116bad6fba58f8b8e2c7e0d1dde184460a5bce9e1d853d11ac017001fccfd5b767afb69691844e8327d39660c06b8916e918f5587f5abcd0f0bc45328ac01b1ace66405ed6c7a5080f5795f2ba0c0d9e5d05f462d89aaa230f240fa47c5c0de0b2099e93ae8c0f94a45f0513ce2797cddccb5216bad88333f6fec5805e0b9474a18ff562e78dfe325c1a6bb3648b41595690cab5ca9805eaa8e13526d036f4233f8181aa8efc76e14031d343bd5d9023fd6cd6b32947e173807883b6a964d398d3fdf9f34a44bc5fe41c5d72ffe5fedaa13fa1da68e19656ed70771529cfe822fc694be1445c79a041eff9753cf47a48ebe9f2c9f1546117b97a1c2bd6e375b957fe38cfcbb29f8eb726f18bc6131b1e27316064980e35d95fd913583b9735c6bfb03ec86e935024e60679a61d972b4aa296189fbc268552acb6cd890c68e50e325719d2f2c99b8105bc92fb582eafc1d618b4e88070e6d8df4924a1780f63ee216c0e937073725353b0c97acae72ebe521fb05fa78a4d6ecb7ba2b860190b6a5c340c83249919c26466c9cf4a9b774fc823141f65196df32edddad59fb05ba24738d81734004c42a7f92f5234b7f4fc0d605b032b08eda4cdfa3b03a3b805ecf5e4ad667a8cf399efb3c1d6822d9d5bb5eece6e08a515cd75c4485537eed6cbd55efb0a3d393d2cdcf44846f0c27cf6a664c70dbd54fd56b20bcc482de2eba84163375b1bccd43da63467557828b791dfb0a976628206e26e9eb10f1e9b9fc34571cc1a38f54909a1ee0b097e0f22f3ca58b56c631708e9baa52d7eb551bd327a6e0014fe98f04d7b0544fadc06c16a5652ea16b6c92a014bdeb2c070843152847293a0a925cf3c22bc9ac23464919ba6340cf233990690c94601876bc30bd5283f97cfdee757caee7b7b41fc265fb2ca9751e3b523e8498d665151d1c67624a084571f6c29973e624703d2e87f1ae5a5e0dbeeb96f46698223e063d10d85fd13fa0deb3891359844ad5e8100036d9b70b3b7831ae78c927a5e2c2ec62992cc424894ccd6f064e9ac307e6a2dd092deed179cf60b4bdc6c56c32a538d477d7917f7203f898659104ad5aab3e520a9f21d9c01b581cc968a68a77648cd95f66b5a1eb1b9d60b0a73c2eb05873f279c4ce43409853df2c8db254fd5b63af900c764ad4a0aeb4641e1a4e4089967e83603cef0317d06d1b990354c11b17c83c2a78cb9152615b9eed33e20f61a83179748ed4044a65edf285b155d41c735a883a481fa07c4941865b86d3cbd54c345d7933e32daa90a1291211191e0a3a5015f0f60d2ff07782606c164485aa469bc97f74dbf1345ea631bacb972be00547074759fab2b98a78cc43b7e4fee587e40e23d9f8ecaf03337d47d27bce633ccd97c0b18509afe35d54b1761a4ecbff6c44dba5402f3111eb671183b5e6bdcd69398056b980c35628f03214e0cb4ee5364e51de27843ad6c24b1e972889d54ed2a31d74000c32fc90b3c96dc3761c9649305c5f5e75f5b5ab7eaee213dbdda1039d4f3f450c77e6434752b74a55c42c98719836fd91fceb27be449d65c89a823218e882b987277622ce62fd0b46130760f10be6b49ec57e25cf6584a99433cedb1ecb1ce25cbf5e2b6eb716c12064f2cc45f4989c4c1b6c1ebd390c44195a16da16340d3a8588754ae676a990df1eaf04336b3252a5177ca3b3b2afd8e7555e92be6f6d7e64bea52c8d25adce3c580b5731574729107afe0e1025e6da2f2b05d6cc223778ac7de1202566098d43d0e4db6df8d242b57353cbadc0711030c6612cb92a9f9685c88856a0739e61d08c8fc5864fb9d8f41b27b43152ec15b3307e2c6f20e00254984e6284763acc977d9e16ffd4ca8be3ba7a289e3c5863371c132b8300f59d50f9efd344d1cf51c5ac64f29210740be2dfb720397941e2ba939e31c9ed563a674628b6ec3c8983be01e41eca61f869b920bf08db3255cc04a1f459653a4a3c204e6ec5496af1984f5e47caf56993bce1124bbfdb5d7081895a96170241307f000ed22ba92d5f5d920ab39d3ad1e288abb76ec3acbda6a7779be4c65e99bc7d7d5aa57d08ca1774e0fb146cd3a894beac12715bf322febeb256518addf26df507db3c79cb2883e6bb1e4716e185026cd760c76038d52a2e0695d76b3bf9da7df9f4dea5323f7dddc8ea1a0031851928e354e703651f7a21df505a4e7f9f7a8a398582aaa9f504510c0bc6f9af8942601bb3a6df3faf2e97c9df28268e1dd466857d413fa0bab4ff017f12554dd317455d6c716f9449c5273ffc959f147d7e0da0c2eab432b83f96f7d2de7b1ebc41eaf2128f8eb8f9c09c7b3fb4e95bb3424b216232406b81abd4c0afe11835a8425304376b08a4442c6eb0e88a51bc67a49b143bb35416c9ce613538483c091bf181d322efe10b2204aa0f0e01507a7cb809cb54b5cca5f44c20da32b2b7ec52098e53de672795b68a44ba667bccac558210307ac27bc5c203a2e6a264e7cf3028cb4ed41e29bc46985c1440187155b8bd737fbf9480749d62481d805463d75ec17c7ba83c60cc7020fe4b110ab1a248f6fbfda3e5b8a688a280f87a53ea7154311951c2001a7894eee171a64cba9b19a21e09540dc5ba85b1b2880efc39c8886dee72e64f045d00d99a90ffc81fc957ee65b7bb93e113717e7e7a275814404198f9e9d33f45335908f1ae917e0322b3d12c3d298ee814d22f8a57304faa93c18a14fdf53f05499239b66498512a6856ea194705521857a1724bc2925ba34f0e2707c84ee234c3436b4d3ab46cab863a28a011236e564ddbab3d49baf77be49ff2f5f2ca75252971a421aa2d4975aeacedba6cd3629efef8b2322b516c60ecca2541112b59d73b88177cd3be06163a463306cac2c0c1b11f84ab9b2fb6ab776bf0dc0fcb381975e4c1a0198b41bf6c1587bf6ca7272d8c722a581bce4494be5431cc5e3038ec33d843b9049b018fffa03cb3ff58e5da6bd6d49ffa6f776a5c4fee3cb258c4bf92e5e4079e38437f321e0c29d608cf5f69129e705e66a7036d67d43fd4a2919271fe07052fd30c049b793d2af0dec2431ea687077cb7a7a5badc21fcb0c2ac10bb37247207596d33d1c2232fc2a225acb9f30be7fe6da19514def9b595929c1c0c4b146099bcd980356b601ed0701299ca1a5547d62badd0475bc76fe51d0565b95b52a801f14e1f1ca62526cf8931f0706e3a478d20ce7eb1b1088454beac15277a72c90dd95f6146f961e41caccb2cddb2cd0680760731bb6db221944eab186425b0c2caedd840be5a2575d8dfd5810c4db5403d4491304130bbc564cb578d814d952706570e56641e8ce6232a2d4b18941c35301cd5c29211237c274dfb1aae379e25efdf827ff6e41cade1d57ed229a6ba76bf98de6592e5ae3ffcd481b53071e452a0b25f12919003474bf4ad2f36138d3d7c23c89b5b41a25c3622bf1dab6762cfa86a5bdeb6f2baa9cb3ea7dfdf9bdc700f4062ce8fdf477a17088bb2bf654e2eb9ab943472dadd73e3b63a4fdcf90fffa98983130e16d512b54aa0d4acbb1604d00fb2adc4a88e87d466237ee7b0fb33e5f384656a8759143db4c377e2cd264259" + }, + "rotation" : { + "falcon-512" : "0x39aa8af7d097a0c1c179367796790d2e6f01dc80a2936d8f1db4a57f4cf4e17e29e341b1a07806c8dee46b3fdfa567968dbfb21e510f4d269afdfbcf6df5dd60a9be3d28abfe2633d4db26f751cc26188b4ed2cac9b3a7b02b7db9574797b3fbed1a94786c57f50dd59ab32e213769521ec5926519e22a5084f7508f197eddabb3ba3a85a192187649eb4ace6b4decc0a3ae576aa6bba0c9052e2e8eb044b3874f8a66faadd717a0833791dc67f6670ee2e916f46b7dddfdcd1f0e138ab8a93dcb4b69626b5eaabbb08f61d0af2765d7e0d8e5500d460a76d2ec9dd4590b340f3f4e559393a13d88aa12501c5313ae58d1a5f52621b2940243de4ff00b4cc32d42921c5ca12246e2f0ed14b4623dfaaff4a2eea3477e3d0e2bbfe0a42f96fc468184388c8b70da9769c49113371c252748a02a789b00e41e2ae202e3c873bae8fbdf1cc9a706e58bcdbfd764272f3585185a4c8f1ea541394d55808a9db4670ab55fb32cab28e52d0b2e793398cfdccc3fdfc72059d36dd73e66c886e68b6d7a78d13f07a79283e088440d789dcf13db64a24b30bfdddf225d6c3324afa0dec9decf4d15906363383a910d446d996ad2b1b6d8de68ca7e07d5df59b8c679d089a2a81f723f3ad06fab71daa1ec8c45712c3342cda4ac84ffc58e35ccfc9cbf498f899c542d6525c8d6bce984dd25379edd7b4488a19c8717cfa1bde4a90cf7573fc06fe39eecc96a73f1d2be1c98c2e3cc590a48b56eb76b5cc6a5d9f7649aa69b9913b48e46e654c625ace3f116aa65b6e8c333af67755e5552a8d11c733e5e921085716977e65fbd1aa4aa4d0f0552815f5c63708d12acec91deff083c594af671bf90320a5f1078540631a1aa681b8ea44322cf7aa0c99ec7816f6e261f3821b58599faea28cded6bd3c600da40" + } + }, + "2" : { + "id" : "0x41f547a71d1404f147606491f3b96e49f67d4658", + "keys" : { + "falcon-512" : "0x09b3f618864123704b78a9552a0b068f395444870c2cbd5d741a0c565554bc2785a691ff502d6ed0e14c925eaa24c075d20949d12f369848e178d50b085e87c9b251f68b61ec506435bf5b53a49d299409f54e906daed0c1a25a63563090f6660380321e786311b492bd7ff684a2650bec34d5bdf071c49f5a050415b2c3579b2994fbaa9a97b7f24a30e7e8bba68145196e0424471de31775aeca732dad506b16ad4f61a325136a6664f439e60687e764a009db3218f706785b98b154a625c168fa2ac541b8dce39682172b95e19424b98ba985622b2a23d8db2d46a7230bcb667372a924aa4d1a9e9cc1a8dc1ae11fe88081d74d857bf63e9814a08c9ce059a4b1ccab23a2fba765086b15bcba135d0937a064b90663ec69ab521746600610bec0a6b3c705277f90d588a00e908dd075059de6c52fe05fd8fa8ba1b3c2cf151cc810225db79aae0328b8ea1e92fc67b7bde405a1442128023128a5f7089722b954f15597ebba3aa3b55ae947be1e659be52af04c3205492387b51e72746a904ff13ae41964281ebd81783528269ccd6d2a7845d790fe4cda8ee58d93de3a3ca28bc4e6e18e812c313452a4468049a86ae39d5fe156fa2a43d29161bc911d515f4cb6b625f4679e37d5b55064a16c3e48a7f3ae10a09d9ab7f0ea4dfa2fba9da412af1978720fc47185c7ae16474833c8d278dca65b822c5526bd4da67c0700166cba95e3d23691e6e1e86ea4e560cddd59af18a689c4dc2b6dc0e114f2a84754498a8e299ac17a12fd199d1fd8d5c31b0a40ef4b2fc9627d16ec11241fddb4fee6c652d0119f7d60e852decd3489cdfb75c10a18f562154e542614427ee7cb0e96b99d74eb9585223760bac37de51697d86403ac877f823451d8c60e84188a8fa6804c3a8298f223169c4c61fe443434a76d6a51ccc1d06df3b962fdba9e27d7723a8390f4672d66dc2f756263ab928d9d14f6d550acb286647ed6dfb091c462cf4756194ebac9c94bdf7ae4ac7b5093ab9cb94455dad8ab15b134d31947df478a1aca5e28e98020dfdb6d4ab750911bb2690104b9606e78c8659c2f511bfa3254d32e65aab180ebb74f9848700a8cba3a1a546b77678a17716f232e57b204157761161670bf654f182a14bbbb1c16b14b652127241167e898e097b6270b062f0607a50bb040868768a24312a8da316dbc547237ace3a160b7835c805061f8f2723386929835251945908058905f625cd9a91eea83c0f693523128c68613038e", + "slh-dsa-sha2-128s" : "0x9fc24e59b10d63d59b6c448f79d029745c4f27a31903ad2a6f6fdd09b3abebb1" + }, + "pop" : { + "falcon-512" : "0x39916580739423a110970dbfa8b6f06ebfe827b6503b1629f4008d64024e63c71af7a932f3f29cbf08eae4cca92f36f670de2f3a0a461999f431bd5da16b868d2230d6403158010384f0f98d2b95936e0a132f5b86f5d11e32ecf3cf206ab9d26c1cb7fc93ea3468109a59163b15391795b76ce450813b28d28510767270c1c474632bd6eab70174350d329d0039ef8fa6739b2f665fff021cf669156e3797da72e5913d08c94a48a16d12b5ad7e521b730bef92d7cc277a92b3e4c9f9cfff7cb3896f93dbd495637c2a47835f2afde4222c8dd36d4d6d9cb419b5b5563bcc1a61b640e70bf2c4bed6599c5ec50643fa302c227ae56adc3d4b044926f4bc4b26d58d0d0778e2b8ec5e18b5cb9ecb6d7a26808962082bea23a932afefd0d483a9952f98ea11b629b0ad74c367e09cc016b817b624547ed8fdf360a8a06acf7b5f0d3c92448d9f7e9453a4e29222611490a7188dcd0a8568b67d333228454cd9cb99fdc9d6d945a64d8d96348120295b8f2ea9e95f92d9dddfa2a91b6ea2c44de7b605db8e3e1019dbe5cb464e0b211713cdcc32ef3631caccb48b6b0c968492a5f1288e99ada3a4d8287b5c9f46236b4b3fcfa55135f1ee4b13e3d6765866a316d8cb3ce80d67a518ee974976172f91eff1cf973631ade2a2b81134c39d0f3c72bf4fe546a7bc7d3f1142aaae3f5b4e8f3932d9b516ff1cfd827e7395d576d96cff52f00e76abe5ba3f1f0887fb064ee11096e52d441d1b4dc7a69462acb7296c489f498c456b2336f2f112391bd7e545629697bd096ab373a34147643199222b8f1b05a95bba5877aeed29c43bddecbc814a6f2bb38d1dde30c352e06f19bf52717236488848599eaa1ef17873681e91064430aa2f4a23445fac0a8c8231c8cd7c27f928ca0ebf7ab08feb53", + "slh-dsa-sha2-128s" : "0xe922917f6a5d9d49338e445586f4b3bfcdd92b89a013edba38e598aad56f8d10875fa62dddbce6c66865eb7d14067931d640a3b7c5dc448bf7cb6309943c2f0930c78c03fad2cdfa1c21689c6f621bfb1cba1968dd28e66ceb6cd5cd4f10b8797a53a8ad54e93febced4dc1c48fc4bf768b85a811866f86f004fdc340628ccf44b295fb5310898614b6a0341a2456b94930d086ee75f67bb935e62c739fb87407975f78f372ec11c0bf8bce5144abbb2678711850174134e6e5acc3b87f14022d158a57dcc7916a8e75341488af6b1d9c0af90bf2ea86440fd1b8001d7fdb8ecb8cf392498e41f8be5c7aaa81841aba7422c45fcc0caadd52d36d1d56cd02d027b6ed9d122ab3d53597507079bd9fb476105b12e3406c1873a8bf6a5b9479b5cab9c370b79425b1bc7616e2f1f66a398c2e38f7a9ff1077918fb3f7e65f025d4feb8b13397460a753b1999b3d351c5b77ece3751686c02db0ea4ff23727527cdaf038ff060a09689a13fb7dc902cb82844b11f8092625272d8888090e60cb1040b6cc5b0ceb6854a7fa769a77523e7a718a1a9cf56b94be12fe8fc4d319b335757c6b4807e17e9ffdbb486d9910404e675648d069c55e96894c7b2ab17d91aff88f44ad609142ea3747117596c98831338c616cea7439b1754390604188018818a5fd2cac7522b2df5e42c14e77b7383744234da5fe1a02a60e18a178751eb9c0212cd847317d64dabe4407c4a4d2ccb6965eb33699c37c733d42e66f92b01cf8b0ba2c8d737711439de358756ab4ba374e7359f8ec5838f531c984e2903026acbce89ff2935855ba5e5f3d85c82bfc3975ba3d4a9721ffaab1182035bb7526f062466895c5900f854fb52f5913591828cef55cd07d70c5805327cccb9822860f32a8e8f78806e39d36ea7be59186da6ab5f1739b6716323b9379446ce6e50345cfc9b769162ba96929e2cf0693b77a558e5386945fe5c6410a2594805695a4cb602fb25993a0359753f4ecea67d0a1045a80054664a5f589dd823d11b1e440813b3bf141f119c38f9595c1eec85ce74fd0cd69ab783ff7adf32746d3c4db1153df20ca1ff94acfcf3c8ece27b9a7819f7751028435730771a0d7eee4d5a05627e9577de7a1c0d16400a81c6d93fe48990d8d4eb942233b6e81f2606c228423d52fa012ba6de7c24ef446f32ba1e1bee5ab78e79f40ddc3f8279277735564901682f03ffbae6176aa8c2dc6871cdea56cb875f25c88d955ad2e5f560fa25acb5d2130327aff86a324aaa721874c4276667404738fffa534f26e4e0dd67a9d0e5d447c2868af3f5b93dad621f528509fa6c3d2b9a391275396d3db6487ef219bf42088433b79d34ce793f024380319f1a0017c0cf821d998871fd35d2da47b774f6c6664e54090d74d087fa43ca622417314ac0a7f2d23da0e9be5242191b9096ab0e200bcee276a787ecb770a60f975c2935f24600869822ce96462d97f34c23eaa50b443a504670923b260e1698eb12a76a094b9a8b9bc2fb07c38b03b495a70e4976d05477ee88b58e2d66729a91f0fc003e56236c5285869a6eba073b36efdac3999ca0b91408282f83b179629bebabd5355381c4435bb173598ce3496dd8616eba46505a64d29a166b51b1033cafccc0c6cce63afe4f2dff7a2cc62a909853b14b40c418d3a180a922b02243637700c87bd0268e3ba09730a2e2e20065a7310d8cb86ae6b2174454ee4c01fc86c0441cbc51f1801a021da1f1fa4ba6cc0e8e35a11a0e55a4aec60d5c98330ea1822991083deb2a80efaf382d29cd0b04fc2b3289e024814281aad94ffde1a837da103a0022b23f223e63b852ce8bb89d50271b29b5dd805edf1f4339bed98ad4e4fa42cd726f3f70c7c434035cd9180a02ac954a1173c2694f0cd59942291c34a8071803a69552d71b8191c4299add494d05e838803e5e9de4e5a2d7b0f8b4a221ce677b5b365e57284f4b74c973c72a565422b2233a5b3770cf28645a18ce1bf7cd0e726566c382d801e655829a7c5e2aa149217075014492b80533bad2c8013c382aae26cf4dc39a6eace929903894f85c8e97e4be1df2bbd53fc780c46ed384e01fb40c515e88bcbeb2005df6c152f0d169a90ce3a43c2c3469a85a456991996f6e9ffab555233598f767b38307fab61336a8d57c1f6753749d46e1b3032532a5ef11082cb30e12bb6b292d0f37806e6208d4bbb7c7cba1b3b410ec1d29dd35af7d38ba79059999992088f06b587bd9570ad0a1e2352a6133a50a3a45da27fcf63cb7c6fa98e6d3d3d7987447fc596edd9a157d72d0bb60441935481c54f9c163b985c708ea1dc510f35f9aa8768e73843ba5e8621912f43383365f534f3ceef986e90664123079f7355e5a5193b0ca1bce0443e01adc00f18f8f07a9038cfbc672494715221c0b8c5dadf7a66875ad67300e5fd662ae7b4281f2df29aa0f5b7f9f70fd3180d7875aaf784eb3da36f9fd8abfcbf2f156392cd196ab952669a531bf941b48b7d2850cb8a66addeeb1447d89daac2079094bd4163d9e0d8ed52e9a93e31a7d4f5d2afc4b7d2c0b988d41c3e247237363dc05dd16d49d159005b42ab9def7290a51beb7811e9f4fb9961a7af09fd4f291f797f8eeb05bf76884ca340af07f62f587dfcbadb344c1b350960686aeecf772f59d3f5c9f6c01240667d1fa5c3d84dae1f0dc9562543e4636d81a3f0407bc29127bf4b4527d0f6d7815a941fb53e6527d5940739bd369786e710f6b0f69b05b2e0c49bfa51411319b9f7b172fc11d901e41ec54887a0fe5748b1d2403d23ed5494b96639126c1d3fde11d85c8c717fe8b01319a9668b109e2d34a6ef5911aeb31c88b71c32a6723b0ad2a28b2d8c75087a546550b0971dde64d3cd69434914d5544a8828c6e7775cc802fd71e467e0327d692cba302f681fdf0c7425ee580c413d1a3904974e55e7ddb19de793541a7a4310e50990744b4655e72b4eb5a3a8c5fcc09a545198c6475dd26a43338753855ae4dbd845d3122b16063b9d4f1170b0fb1eddbe15bf6a0f935565ea46bbbd6c00491a9ea8288822dbfe8788aec3eb3e5e42140fe3cdf16328587b736242c2aa85828dc84352bea3c6d985ac48437bd72f12adc7fc08d03371992547b91a3061c9f9bc6c32a8555df262c14dba813ae8e2f9c732bf7360b1e94498f5a1248ca4a0c020e381d8c25f4924f91bf0c649207b1314bc0088d2f15f662a2594962596ea66c036677c451e77f051cf9c5f6b83ada58109f68b415af541dff5d6cba732a938ef4d425243a3571f475af0a456a7ea5afc7c78fa195780b270ee1f4d69fca7c93087e103595af8757afa5dd23ed17bd723c1edf8701a1b4bcb2102ab2aaef2df7cf485370d310651f6e897cd2d687375d29c7f101aed941cbbbc12ac7ad60c3147e41fffafb766e548f0e96bc911f38a6d1864277c170fae28e4b06fee1d1925923432bbe28e41f8cc19271f68196bf4724278f8492a6d5c0826e03d8dc1232c0efe5c7598b71be7d6c468ccb1077d7c8bd4630bfa4a108d58d5498e40584f6ab690e27ec8152c1c39bb70ccf7c5f2c33e48af63d536d9e7ba90db931783a9cccc05c22dbfd90f84f75d139aa22b1246a15534f1521ecb9031ff0686c39b728fcf6e0323cda56b641561a88c6f78cb1d880d102d26b20213d03af41c91ba6924a0ae17a92fdb375492fa0b514ce2fe88fb95d72e5b1b11bff99d1b0ea0832a0d3472b5880b3ddef0bc0891833adec1f7797f86fc549b22df2a0a59390a900e588cdd2405e5afea987db98554585c85fa2428cababff59c17713cfb100084192cee9f4ee3855163ce76fc59ebd4b23766b0ca7c2d59754ca42ebe3c997cc4cdcfc2ef455d3afd488474b4da2a6ba278c196616f995c4398710c291efccbe36bb724bff2d097095f440f79917229a6fce31dbd89e58696c4571dce1e535adf5e1b0942fb5d59a13f7fb4eb17647e83e587acc50d920db2951bb48e1495256e10a62a8d43d3cb7719c54c24f546ce4937e2ba7b1911f5684068c365398e41f7153cb1e2b9ac014bb7395a36efff14ee94373dd128d725dd6ae6af408a5da12ee540fd8d2264b24cc7fffdb2859ce5227ec3c3d5e0bae49b5f495a39e5d6b3baf7c967b21a55e3528fb0bda0e9f2a3cdc37904005a76b07d044ce439a345f5ab69a3db8c711d478288df2d4776d53ccfbf49a06d0d0332814d34053c98ee3464bbf46309b763114ddf1073100c34466cbf3414b4bc7b8ee73c44d76c46435c0354a71fba7d8ba8b0248f867cee79a4beacb5dee9c45577e5b423582fd261b6164de192e98aaabb47179a6fff8827221a70c77df46d01357bf27392d4dfe3b8e24abe9854d3109b533edab16f495eaae07562dfb4428cefbffd748f144329f7653931a9f65a3b024bf19d2fe04764d53730df662469a19c4bd966dc925b1ac52d9094ff7dd7268697468135000a8766d8292f9079f9bb5601193df637f8192812a9a97f604015ec15c89fee9f700509cd745da588a86c7c756bc84745c593bcb7c7d9d34bea98d505a57b31a90833e3d1e154c3b686d5d8a20375111bfc6964bdf2f815e160754b56c59fec8fa533cca9e21d4f688ed94cdab427f9e179fd308856ab7f7206d13d65cace5f8d65c8b95270559a7d33c76c81794ec38d75e2b3f9ebd1351e07da0540e3981d447558d6c21ce6b0c32bf61a3f554cae9edac7a92e2e25965037830c57f30a021953d91c8349b94181ca8c3bec9f33327caa08e437e542d4cd9742ea16801aae94e19442030e8c95bd4f23d88ae9a1f1cfe4a80b046f2e54885c54572a215f17bf8359db4edc830c48941153bdff79a6777a67a9ca0ee3c5d2e4497871f4797974c1d932fa3417b909ca5b29dfb315bf1590829f1cfad84b9e068213b8a6e9171c1c41757e262d371b7073d1b90fde5bdbafab76426f486581bf52fb2a53df2fdd0a6d5065afa6148606a3dd311b155dc0dcaf9f741a50d105053225f1c223ad0fd8af47d27db808cd32f733fcc8e2655523fd36a906c6e6a1d6eba56311424458b446bc1b77b866af76be210b9a417e87903a1ba2e431c176a82cc5a71a4ddf29350efa6dd8d1707d3884b31b9b52bc876b6599f40b077c43d815d2e02ba043505abe7ecd7dcf73818ceb77a09ea4e582616f4af11133a80429edc9a434f3cd2b41192edad842b433ee7936fa5c7680023f0727f4337c53912fdf6fc7e6d5c0dda2e0a8722d5b0565ddd8f9349815bf38e8615b5c7ae35622d7feb7907e6423c8aa7bdf57355c0353da377977e180b88ecb1faaeee2d7142f342263e636eb99c78af9cd0ef0314d9b57fe1176bf8b13c221b5a05c1f876194ac26c35b0ddd91a2130276ac402314367d99c2e300a33073c06dc18cdf5361908d58a9553268c6d0ba19ef6f6d0bfbf2b1e2f5d284c74695189d8a3236e927d9598a4be253d0492f98015851be3693b0c1b3c4a73a95ea57c20699240887d501db5d3525e787dd1a583c2a831a9b59886c873b8ea5dcec376ff37fdabfa21b74b3968008dfd9784fec35696779217b4f5507ea15852f751af31243dbf857d66cdbfb2e290b52f67312fe5d93bdba25b1b7dfb0f3488974a7ff0e437cea09d0baaba7bbd2506fcd182cf0dabe7c262a823fa1dcfe00bfaf7cb5f54283763a45134dfabe9927fecacd648ef767fdce12d570693ca6aa10c74c41761f64f31139f3aabbabefe6c7bd79a6a3334cb465f4203604c0288c49db25bd25c6c4c113e09fbd65c0f271f4a373294831c33f7b2ef392e4a4b91a3973d38473b9f3a485cdb04b7f75a833e2c0461c6f27e6d8f518d3a4366ea3bf37f43e4408f00c4c72ca8d0a9fdbf1ac7fc9fed47e8a3f3f33ebcbac25fc2e70bd39a85519181383597ccc3503e597a734c98e98a15be8bf4b37330f585ea138e5a2e4104ec1cb6f5a23df7ad83751c32432033f072e8ee3cdeaae4e7de98f9e58633bcdceab69ff60004cfdf51b54e9a33232e1df86ca9708811a69b8759e5b2daf57eaa8f1f74ae6aedfd9106c105a6cd715b66f561c0a0500a501a9f3d50a0b5540ec96d24fa5f6157f5465d413b204a224e2b752a49322a90aa340e4422e60a449cab5b6106135ad34ae309ada87ce9a99cab959f6c2502b2da99d8ef296884f4255fa88f38908e4294f799afbf3d664900c5230ebbec54328167495caa99942c13e834da5c488f2dcbbe4d2448eea2657a2f82a91a53d09b2b5445de8c8c8ede11f028e067620f2ad83be80d19f3b9a9f3b81bc8a405c233b2b17ce8ee5e0ef05847aeb36f219817d735faf6092d13569cbf069696bcbc50cb50cbef921f8fe76775f1034e73bf04431b696eae4943efd78ef6c19436c76ab389825e57b23f4529fb848233833afa418d85a92fc865e634357071bc415da56078f44bfe306d3b00574c04b2e94d6b801d5e3bc03686d1bb27dc2753112b2c22a43610a4dbe1e78c9f4f6f2b4ca3ccbddc2ce0e78fa34cac5935bf0867bf14f40648c805876e61fa841292ef2d2d53ecc00319a6df0bb6347dc865e39d9da574ad93f9cc910a9eb920986d5e6b331cb29b45ff32ddbc9e09b71eef73c8673307d6684a34ce85ff3a4208741b205d31011555b17b7c5effc210ad9309468a007d76983011b88c818eaeb3d3815a02d76f735433192f1bc99a6c7dbb57e107caf9e986248ebcb2e9c28279ea1e19a8f8027bd6f8728c9aed0b974322254e686526cd9ee0c6720de4eac01cd83e5705570c249856ebd7ec76cddd98cc096fb93db8ed86a5aa0736c36d978c09b915f7b9290cc255ea1a4b355888fb96e506cba34683c6a890c77d92fb01bba6b69f91efb3e4313111dafc3a189db063195bb650e0cf353dd975fb5c286f86b8f7d982ca5f74a78fa0fa3d693980be09298bd41c4ef38874ddb95e928494b9e345525b00e4760a1d6a4d54675e39b1d955e20a58f3ee65ffd1e249eb3558ba348a2be114f396c422c57314ce6df0ae9406dc6bd0bf5f1db49f012a03834b3314dec7839860867033643aa7dab3fe17ee025812ad2c05484abd0bdeb67043be2a57533d294aa03f24ec8afae512537343eaa16b07eabbbd2cf6755261ee473dc457ec36775102e1c8150248e4e51dcaa6abb7753920bd5247ccf6dcfed9dde98c9f37938d1c824fce116f39f0d67ab9c1fbc79e034e8bee6f79715433d485edc70e5c098ef7bcf2972171da06cd2f19cde8f824e0f2bbcd816f7eb87b7bdad659a900f36df708d3446d8e412b92142d8414f3a0508d106473e809a0831953e2064c655519a8f22fd0c517df7ac44d72a0e7b6fb697b188133fdea4d16e51c6da3109f7cec1a43fdf47957a35f4651454ae04da9e9d5a1668f46cbc58422c38fdf204f4e9c0df3b1f0ed2e14d0e008bed642353ede25a5c25684e7d593eee9d14b3a5a81429b30895ff5d1f7f4a124d2fc9bc1eda61e8e4bd73b57ef1a168cdfb1ce1d70955567a3d949bab203c379e3676932f96c04ddd0271a6afa0bd55988134de26bf5d6c725c09e04aaa03d393a82805a366e0d98e4b65bf45e05ce45f4dbdc1f8ec052102df08f5852630b706dcde9a3efebca3852e85cbd7fb6e6e00b4bb397004db1602b7f8c0dd365eb2d946d00eacf0ecedae83d26555a1bef8be88eee9de9f9811074151f0cc747f36d45ea7290d53c18fa8ce73f77f54226e5b1719a9bc911c9d7565693836edeaa6cdb7b2e2f9cc8c86b7c207cc14c261793df6c4c623355db04d5eac01c011677d26b764e9048bff8d76e2b6bdf792c57e044eea4307f5a3a62900df3f57ae88577644e2121134d61bbf3100fedf83371438b7854d06340ffc00383a0a4789a6a85062aaad3761dbb3c77e61c5926b0488f711a77735dce429b6e028373dd341fa2db5394867242331168665b4d7fc4ef96b12a571b8b25d44fb0de64b9820734e5647821a038428d67dbe95885526c3f2b8064e8baa5144b7fabbbf5a133c4b8d7a2f950a4fb69f9cdb37ac28cf0421894f0b771f80db988e25fb4ba9991664c93116bab82ba808693d583a2b697fd2b5eb673f954255da410df58231eb7c89b8257c5a68ca0306d088638b2faecbe3cacbbf77725e1685aa81d965f1ef663f1e474eceecbd52943c2e4e0456b52aa0c7758cd2fe67f3132176113a16dc9bd11d6dc03ee643fe8bd6677f2ec0ea754ab499e00ced482d78147ecb2f91d3154e03b57dfad4e4cdab98e134a609a7512a5a0e7c3c4adc9db444238e0fc3c3586b389d8d090ca46222a9533672eabe4fabcf61beaeb59c507f2941ced1ee4cd2afefee5aebeb354ec33cb85c9695426126f016929b471b9cc07dd6ff787fc2d682cc1e80657bd656acfb865dc91d6b032785cdd7a1d66d4d5463e142b6f028e8fb5bd6c621109e7022818b39197f85fd6ad4785efe48c4509e9a537f10d4fc9d23f275fb9e684c2f827868c9ff3baca8faa59903c829bbe1992541d15d538e64329e886ade0c5517cbab3b15fdb4e82506e27f449e268b488de32fd036fcf80f6c97505a5f33e4d3a004772a8970a3fe54049513cbc57131ffca1c82918a81bcb879b8f48d4300b11cd90069912ec9e59d10b6bebbc24ba1061c62a8762156df586a4aa758f13cab5ab59a3b1ef7d038c68920eb0a3c8a673c27b6fb80e5e8741b3b7fedff0c068a83e72373da07c8d73abbd9fa0084bdc92866917b546dbf7a6304f04b18d9bcebe2b4bcf5ac7ec526abb4a52a19094c8c231668d063c5558bdef0436ac418f42c5f41f2df996d19d48f8bec412de05a6a3b079e5b6ac1be0773814acc3d20166874ef64a6cf27f7fc72e0232031ddef1a6a4ac9897a86605550dd635c995626fe985aaabdd937bafe3e2a4082f0fc875ba7ea963a8e5987ad400423967bba1c0e369263ddf16fefc88901be069fc617ba6dca90608cd4a9d86476f8cbb41fc8197a9ee6fb331aae55959d107bef29b4f6f064651964999fb8dc904b8193dc7582fd47b0b0fb912f1c0c3838dd489c374ba5ea614cc80d4d196291c4a54989ae628dff5beb61612353ea0cdfd75962ab057bac887ce7f5df01861c769b88c7e1682f0ec6f7cea63598c3e6f41093d845db95377654fc56702a4110810917521ffada7432b7b16a64699e6a955a4273fe9220976f8b1ae9ea79678a3a74344a44c32454e7bbbae9a1fc440811693f66d63663c84bf847e0a2e8299fb0a6413194231a666a2efe5ae1c41db1cf12dbd8d58408cbeb1a9507b5b09ebf416f712b0040f8300dc5188e673c5ab1f13156718031f25cd5156beb8e77e95b48f42d1361da25a94cf1bb4a2a5c41f8da5c3f6a30c00e7d0323f7bc14583fe20815f468928fb69a480673b6c775b2d1af6a176abe5f6c679aae41ee1d5c89439a798823b65ef718515ceed5a7ec87eff54479a9afb48d0fcdb9a240e946719598512220d2afb20840e3680689cbbbf096e7d7505d1d854faacf024935a672278c5dfad333373015b11571b2a7329a0d2fa792d838ba3fcbe5303f84cc6d0454b89cf740bb0f83b79c98b38fa17dca42c5d57d8e86cbe16d46c4c0caad1f55ac4a399d9755067b642299b6b023e47ef74343b9d856545a51907f86034397dc1cdeb0856ea9fa753ba82c5f7e44e4423684f1b03ec59e5cdd96c03dbb1421e89bb0d1dd570309d92519e2b90fc83ece91ec6b13134cad1272f0fb1092a21e0066cc0a783e566167b52856fa6ef12aca963dde340e94f04980279f67a1eaec339d0656c09a3fe91a2b1885f1c09e831471ebe78f2af2efa1fe5821034ae172082de7cf5fd8599f7c575311d35248f2b0e6342cf7ac4bbcd19942a74ae9a9ea3ef5641f70ad1746406ad9bc24a1a3ed17dfda75ad54fb920ee9c92b792b76d42cec2c22fd1bb0f7acfa398d82b5f016f2943dfb44ca1d96b2a54ceb7595837379d62feac76dfa610a82ec947047176aa74fa8d58aef3d34e79768ba7b213a8c44c6e9739f05eb197148b2d2f3c5a1dbf0d4ddc4ae2bf0eeb93d2d4d0990c2c21ae34300a3139ccb5cf216b4031f5be4fc61b137a952fd516e45236dc791b692910a09a600fce9c14ce9e429ef1ae3ae737ebe9921d10522a4507d0794fde66f639189cca95923c51f97cfde4d57c718b6e0ee709227fef69a95a229ab54a4ba9d858115ac04dd0a594ee8fba7d29a23d8df53c131dbd6f7f47c6ec482cb2da38e98a8bb51a2878950877408f0892199ae4e94c4c8eeb614eeac1435b2947ba67c30ff52ff4c958eefd318814a17f53c8ae691d3fd91ea8196bd82d4af9fa216e32636d6535d5c68f579d3066654f2cc067873fea5d56f9676800892f210363fc790086a778fb721854a0abf9a2403ac6de964c2eee9011a44cc0d66ced8bb0634cb986a12b0d5d875db0f26f008555f7ff57f42a441250dc5d13dc5342ef36c288127d46b4a2cd85e5189877003589579aa787c46843e47666be2ca6e8aa84e065ec77f0f77e5cee75785e2358a5e1fd7d540ba1d36052964d7a5de9d1598cb71b238c6a6f2a4b6c72d8c5b0a4c2f17342747dcfb2bf7bc52256fa21741e1f8e7c2b749005ef240296ff9935f75e943c2e725e9f54636c3466450a49eadcc6f330558f2146426fe6786f4ac6fed1943d616474f7665e8ecc2612479eac464c8313066de5fb652beb42ee17c0d9eac4496b5a732c3df31d90e24ffa8eacb12b72dc1d393d0880959e0ce9db8141ae1490bb12af9bb2d12da5809a348714024d7beb73dbdeb4d7adcb68c59ada159fd1855ddfaa33e0b985d7bc3f509e64ee52b5057656295dc669939c45d89a07f03c0062e20e309a0388725c993f74cfbb8db38b85f13bae8b27a4da755398f228d79eba2ad5101d80c75eebee608dae268dac0c3a1c63e311fd82cddb78f06fd576314ffbee8ed598818894fffa54b56e7a7be5c20ac553d106609f4bc757acfe7b116f695fa4ac3ac93bd0de4bf87aa6aca5535e467298aa934c11143bbf39c464db663b97ad416b6501daa12fa8d9aaa31334851f36f654356467cf4f42fcf0c4cb239c2acf7c7938c0760a6a8f953e75471881c59293285241f7b73e4b3dd5" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n12b-rotation-wrong-key.json b/consensus/common/src/test/resources/aere/registry-v3/n12b-rotation-wrong-key.json new file mode 100644 index 0000000000..531040d2c4 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n12b-rotation-wrong-key.json @@ -0,0 +1,51 @@ +{ + "description" : "index 1 rotation's falcon signature is by another validator's key", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : false, + "previous" : "ok-epoch-a", + "expect" : "AERE-PQC-REG3-12", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 2000, + "count" : 3, + "0" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x391de378d9ee4e1d810e6ce145b1819f1b73dab7f6dc2468fa65d1ddc8e2a7e6fd8f185f8945700a938cc0c5afd87bda48c1b8167c3e6607ac864bb2b194a368c33cf8ab6a5eebb9a704d4160b068184264c2e59c4f82b9b4eaa9197c3ba5289c429e0f4c548769279daade1286d354212b1ba2ea4818c974c17770dbb59b65b3b9f891c826721f51a7ce14f94e2ef4cab868cf852ed9bfb17cd33ebdc4d688d6ba12c74aed2c8104d117574642af25e9bc91ba3fdabfdf1ed13c6e59fa1a0a4388925d2254195c666a6c235174a73125790bbdbed463617757fcc17cfd32da7c7f89e755f051aaee186665d9de393a82508d15f88db0271495c959bda6651a81a04dfc2103364f95f5464effde48f3b7ea80af9c1bbee99c9d39b0627984f09ccc6a4c888d121ca34e56760b1c3178e48607325676c484f4d666681f0d2454f7fdc59cca37136c5c06e746987e6a116728f8b0ae1ae9a32efdfcc956fefa55577ac670bc3d66016391bbb833d97f91d3a78696370a6f05efbd77cd50d4697c3df5699277eb1cafc1883776d9ecc72a763e5b65dbdd184efb9696cf58360e73f2d4db5e8bc6ad65bbbe7a8a994f5073fb4b12ad9b5196c450e27a53d8ca231f670966a238a6223b47d90990b1f6796f93eae971d4939d99dfea29d4ba2e3feffb97c0b27d6b6d2ee10751e75c658b44ee71abe9230f74a0d7f232329b165f5b2a3e584dfedaafa9ab477c0c516b6553c58797421f8d1e9236f992b61963c108752b1c43278a9b910926b396292fba67211e9a16b88aa9bab7b6b6db29f30ab5b7ef1568d46a010e8a27144a5e958022be5cf6bf4305f14e10781323acca3bb1d6b567d3fe5f652661dfc54d71ed9e3969289c29bff0c8972899993505cde2fca11c821d9280abfd5c20d4fb53380", + "slh-dsa-sha2-128s" : "0xee44a0de25b75a42e32080200c89b82e2f89551d44da272720c816fb144aa42ca29cc7a0045d97c82676d36286a9f1bf4d5b9e44b6855d90f25e03e3370cccc7665f3c7fdeb6f9565b76745a7652f70b7cb18989582559b81160fa32c7db7915f14c657ca37224955d174cc69de41774e4ac576f362306f3383733b322f8b333d9d0dee44d80e36a86491bfb7441a3c66809bc600399ac9788b4d7796604d4ec423c40dc1a6fb2b02db9a0d35072cb5aa617ef769006c504052596186ac0d504561c815617067f669b29757aed1de7a03c9c0410fb8b040dac268aa95232533eef1f69cc0100b33c9726d55af73b5d608b501b0470c4e9340b68251fc645bca481dc2d375de626580faa7b7faba8f8a596f795481cac23240854839a1100cd92a6410291ccd40cfdf1f70981c568b32904a0ad649de41b32299dd5a8bf50c35e5dbbd474c258946881b49e335e01c72ab6654741e657bcc5109fbda7297999cb4b272302739ee07cff0d1c9ef454a5dc5403cb7c170337336f94d56f6ccf563af44bcc8382de301720076a7ac1edc1ad95dba129760d7e9cd4df609bb8f7b389e7ab1e85e1b54fa5e3f2163afd7e24c5e5eb61a9e8e79dccefaed30f5fae83bdbd8a1673b36ce894891720b476652a26401e7890af9199bfc74fd0aeb6622fed8e488b8151a6c51a11bd808702f945ae9f9a016bdd94f45279d4765bb376df7fed5506b52b74c867c087c3386f74b130cacd7bcb4e832b697b50c796fa8fe4881c3f994820799417885af464f4f1a85989e5e3b54c3bd878a7ab054bd08c1ad850c96f3c0105380323908fdc94771609b10e489a8445f751f75a9b617cb1aec4460be2b53920e52f7d85096065fecbc6f19daae458f657dfc894758f3d9a6519fb4276e16b38aaeebcefba89a7686546901c57a29b95266d7ab87155a854ba9b269ac94a279e9c4bb38e6a4928cf6fdc3984b4363cc44e0e3cd7a7edf08388b9ecd5a04c55ac26b98f98cda79cd3f402caba28360899cca559be68d7a825022f9010e65f9a6564fcd0ec1cc4c0f4029086c90288fa83656cf2ca2cf30d5c423342cd15b9197905e9ab30a9d147aa2599abae71070e40058f0787ae39f39d6e5c1a8184b2cb3b1e6e45c40c61d46af8490cc9af6422a439f16adfe93a3cbe88f63df5eb8fcde9ea111ca96ea7be416d030500f2c7cf967dcaa4d3ed76a613b2bcc034ed3aa84f35556e7a65a8798825b576236798318034204f041dc59a9f9c407d34c2ca2d77470700ed6442cea5282ee99ceb71c6bf074423dc0785a39b89b062f89231af868812777536d0b144212d5b4b6fc7b87ea04fcd6f94438704f985b746a13dd519c57260f87150db4dc483b664944b9e805197df797be099940fed468b1e214ae99a68e99c6b5cf3d30fdbb1b3cc009ea58958eb4c4a7b7bad6b34fcea2a87eeafda01dca5223458affe54422ca70ef541ca8437171943d5bcdb70eca8de02b93098653160cae52f4d72fe5a6a7ad51c6465388f0362e6e72a3c96b9cad2248e8d9a2cdf89747444c7e51e2f89eb19daac76da37180e4133e0dcab15fc72c7bb90c1024d03eb2e630fc6d2537a101961b388e32ca5019a626b5a23914b06e7822905a5e3c21369b69fa63fc9f43cf7ecb6749c668c7a940b82f88c71802ce6e3c68b5c10c9249ba356bb3bb0539b581a8bd6ca141e5dc2b5932251746c1fc1c3da475581f3e3717f5d905f5ff3c83f1523f57cb750dabae0404beb55aba1105b995268906131835718cd43240bae9aecb30c794a8b186436cf752179b1de65b2a87917147612e3c95288a3e816238b3fccccae1da8041b4f784ddcb3d37ef3f019ef997bd8a4e172d397106a5adebb166c5f565016bb1c5f7f1a0cbb5bb22cd642fa4390ea640c04a968fb5a49ef929df1cafbb69c124ea58b31bf066be388d7b6d4c52409581da785d6447fd72b54949e4c1ad08fa64b520f2b8df83bbfa417952b87fbf802652a65df0d388ea32dfcab3227f645b6b28b66087819b4339ad171a48760e2bf90f1a01184389e23e7df583eab7c2dce385bb7a4d60ae5573966f916bb84aaec6cafe710f839b103532f9a7a556bfa116a05480386ddefa2858ac7ebdb58664a0e633a53e1685d71d5aad11f5eeb3d9ae30ab120fb88f96f383bb86917b72e02768f071ffca7e3fc2ac79846a36f6bd72c58aff03f9e87fdbeb65c8ce5976255227778cfa40ce0eee943b66a382e172abc814ebbcb0bf49efa75f87ef8621d227224b7be51b73a928c23a8937570e1a046bf1fd4c6993a9c14e552915953776265f78882ac8302f70900f9d612d2e7b7dad88cdedcdce7e615baf89ef249d6d535f92a2ec90a7435785f4d0c7a75812b875ec22e638f4af1705cc362ecf52c67900c888d068dc6f72a161013c30dcaa49cbf25c56e8937aa4897451061dcc7579ca99a25348e76ff121983444553419e820a3d1a41744f4227dca054c454a6497e0762155e2a06d17acb44ea55f54e4d6d2e1631a63d9494cf69c38ba66deb9d95c0ee01950dff8e6a5d1c46fececc7027997b8ec626f8405ae99ac20600898fd7fc422e6cd3f774b4009f371ce38e1f089ec93ec0a48d6bfb90ee79c3bcff321eeea33a44022ab910458984b75c5089cec0d8beec20c4c39311b991154fac7c0461b7d667488dc9f0f5497cf66166782e2dfb69a71031fd0251918e4c1243aa4dfb9fc2b6123b4f02f436c0f8737a6663af2b99fbd22c6af539ca9389bfb4e55da2c9ad42521ca6978f71991f53fc8cb227e228a62f3e22d7c3eca8ecee260e55084ad3ace0d6379d5eaf82bf6d3b69c4e74fade4210114d95b41a4c004858119a9a9b178d5a0cc97386bfed0dc6edbac5281c2842951a0c4d7da10207523973c676b8e6887a087c1c2f156970cc6db9cbd3c8ec1c9c1bec41fcbfdb4e8929b67e6c1939bb990ee2cfec3197b4ce824d25a7b4c765945e85ff2b096037adc4dd763b87850fb39aa98f1d395c9353ae3b5b53842a7056c011fb690e856401fc6f67b1c0a5414fc229284e74c230ced260d4fafc5fad153a5154d35c3bf9c559239ce18ca9810a1254a0a6c2960a53d838431447739261dc3b055a0b115914af263ac9fce2b512884620451156c40cd84b7bb2d449aad243a8aea3859412476ddbb5eb73028bfc6b2a942839e363262f94795aa45f6579680997d6acd7a8c8daeace08362fcd420f2f8a96801c002361b942493cc5ae79884eddee989af9832d55319ec033956660c5f11b9fbff9f1410a18c0e9a38e9261bdd13bc392a18a3df97aa33e7f59c125f88d78c86058f3c7e9a9d3b37bdb1e05d90375de0090b418de3ca761560f079a6ddf328e72cd89dcb63c9dd0357ec736782696241e2095aa6107a8bf7bbcde26d5d14533f28ddad7fe1b3f0c181c0e6d55204f6ce79cfc557cfb2d54cd2f33adf17a837243b51705bca7ad8cc452e5340732e678274f9eb532bfea122cfd2959b2499ad25a18db7dc387e976f8b5510821cfff778be799b80505cf38ab1ba6b01561ff81ef17f7735a1ed6a319a7f571daf92afb7490fc17064ccb6a8453ae6e66a2afdcded0c7359461278c49d342df84c43a0607b7327853db675e4c9ca95c52ae013461ca0763d157ac992e219a6683a547c32374631b53138676f50ae0d30588fb25de808e1d9efd5fb9485192428477c2f64fcaa862761c7f5505cc650910224400e2a2db13fbea0b0cc358d163c6d4a8e6296e4c9554078a55f23e60de9bea12ac199cd19b97babe61b2a3b853860b4441ae8dcd85d59695393816c64bfc074e945452080e15bcf6776cbb6b251c13be63932702468679b2bd7e1693a1d910e1b62be91e177a8dec8e4d7642e88e513e3a871ca502854ab0fb7fb8a099d0e0dfd73064012051af337abc78bbbc6fa2dc7169ebfde2d40167048d78f12cb5e7c130a2137a0b26922553f43aefdcdcec55bdbee0fa752a4787ea9d1c75d1bcd9d28135515a0ecfa9735901a0eb89f915d036e21263f90606f65e62f6e5c8237db14d570612611b3ceb4d026ac13f473e5706cd7975c95d49734253757f9c1e6aca9261cd2b95722098c5f7c74daa6c926ea4867ab54e4605a15bbf7ab94348d9b45c31a1fbdc7f420cccce25abb4249cf413008a01e53296318414dfac822cdc0bfc4298273f65cda3fcd61236e255dedd879fbf20b8c4843e27ef413c7a1ce2c714c88358091abccd7fed337a7d4f725ffbf7856ea4694113d605b1ff7b2bdbaf60f7abf7d43eebb1e82f4b6b7f3f8fc23ae27b09f78a7a3b324c7790431c800293e08fed2fa49aafe8f5670d4e6bff4cf6e7924993d6e18ad87699da99a2f2ca9f3c7b44b3b61dcfa5f041f7a6df94131b8e5c04069678541c87872daeb2943902afa6ff4798e45a75659568a02471afde44f8e116fff4eb7c16053a7aa370bcddbf5229c231ef8f2e642c1a88f90e0a309d24a3ab28d3adc176665e522cf5bda0285baca5bf2a0fb4c0fefff3b8ea1522c9ef29342ea57eb4f16d69bcf948d66247ed2f2cc4e567476f736d170c98398ac7c0d158f294ff33ec8d2794ec1929c57ee6d7f46da8e40337a2b380717227ed82eb13a14d129069e98cdd65bc34472fe1a52dfa7e466e559705c53d73d97239e38bbd600aaab107ced36fe7d8ef2387a05d8548a48a684fdf02b984022aeb28e8c5eccd1941ae19d2cb12a31de296b4fd1409266c43118848962e9e59a41054a52bf2f20b09eb4e77cca72df70cdc4d712d5c0313ddf9ec8df2d901145341b3ccfcbed77288ce3e4afc677ad8681c746eca02e7f7538d0108b68a922a21d6a8c2cfd67d8849ff5779de1c00588133309d4158d1738e508a1a2ba3351a46fd3f86339d66b1244bc2788de3fc60144d03c08b0ef08914088f9ce947d2b230ffb9ce176a4bd45b0c8b100e17ec7829eb1ce10b1a514b5e33d4f40792f0a300a4c3ba7a912db8e19cb72ee02ae8ddafc2caf116df905640d0cb65cd0c61d38e0a9741d3dc2b7677e8ec051d329183c954ff9c0c2d8771182d5550a742ba8147ec6571bde519c2f57f10aeb1c8900f8324f017f3b1dad92fb0d19a76de75c0c7cb67d079c8bc0bd7355fa25752d5d00ab00f9ab332aa23aaf8b5cfa4d2723b2020c9e494208c61a76e18c9a583172d6ddf544a327c931e8b908830e46f184998fc0d34ebe767e4f5d06f99804759c7e2b76a8baa302d6950f90194b0cc694a0d351374cd028ba6b0414377a850ca2f209d13cb7daf43d14f606a0e549ce14189023fc7b45e2b095a3f45930a72296eced3e2da24bf237959c0b966bb88a3a9818932b1cd41e8ee002df515ec94901902b2e67545dae53e08810b99dade7b579b847a6570211687a62a56dcb324bb442c4fc22f9d85e54b3f939937daba84c86fcc36c83ca87de70fc7f4afed335e8f9aae027e854e3a022a053c3b22be3617240f1355be9af3f4c8e9481005cbef07bf1380ddc012412c31b221275b56c3ad52cb2f7a9b340526e6128416025d4fe497f2d502070653d36f924420c0844b5560a8728e4a3be8b1ce67a1541c00e97aef9c808bd77af5aa280690d98f5ec218bf5c9d135d85069405b82c221d834b8285402f481b7d51ecdca91c4c2b27c12f486d382e0c14ec23c2bdf33ea8ff693af551fbe55e46bd84f335a68646bd092f914da4954c57201d7e812f4974a553f043ace9c6ce2c609cb4d90dc1ceacd5d610cf4723024538cfedc1bb22c9663ac541f89f97b37d2e1b4d8073ada1596e684b94e74c2c2f0087251f1eaa6789ece436b2940d3285f297cb7482aa9b8c47118a0b1d2031fc311689828b64855bc17306529e4b0a3326438b42698dd4e2e0c2f624aa510a2363583cbac47fbf9974925d81af9534d0ccb41e2b445eb19569d703cd8ace661139fa344a5e097290c233114370831d4710676f7e93889ed533454928333655b06ac5ae67f301f2b7944d769892ed76374a1f3861c8624b2a302df1a2f1a60d41e61379ab3681d8eee152c251e1b7b0806aab2f287338b752918cb989d864e9acaa92944f5194f8eba19a18a025157d2c3988163baf8b7ca9060edc2227ade8e05348d86d18f9eb633e4f06ee346e4c2912ed1c763a75fd1baf32cffd0c856ff9b44f1470136c0a8ae0b88175a7e7f993d8469cc8ac3f643aa7db36992cc97011be0a42ff1df8aa7cd82e260e05fdd67304644335b386c7825858a438c51b63f877af005f7d95202b54a2130a1f652ff7addfb798daf4415572376fdd910cd099066e8d2fe251a907e41bb950e960fe5116f4a4fc6037273250569b41ac07002ecf3b4b63f8b853f9693badd2e8bff695266095f4b2f38ee4d60428a45e7586d9d47c01c12c5e18d1ca9afedca6f03384567ddc730fb8fed27516eb7c54135cc7447af831f0c37727997ab2b01fdbc5e12c112aa987e95cf5aaaf44be14bf4882f49cd84894d555655d17aff21f077b2cca1ff0922afbcbf4781145cfb87df585e6af1955db04843a763fa735d33b3df836585c5894e5125d500673d5e4d0bdf6708d8d28c80298a9bb47c7430ae8ec52285a4c14371b3f8ac1e21306b34bcb0c1e3aa7a8dc8955cd9cac32a0f69d0fa0aab30abcbeb2c21d655ccdbae1c2040faedfc7a5ef29cd5d9227908335ebd257ae8c410b1916b9a5bf0e72902c2bcf701a18c1fe13695a8435b0d0129f3db883f428acea8b7f274a629865f1849d4c263a1536421875ffacf058f0128f0d012fd60f2b3fa6b71fbb96f3e1e33faa09b1d20b0919018e91a6bd9595e9c16d4fa6665c4d0adbd77a59cbbbd587029a4f306c887277a7a27748a6f289edfa16c7e4042fc81355b85a994cc6784f5fde054f188b9775836594abd82c1309a66db4388ad49f60794d8c8e1fe279f8965312a1e23c2956427bd6e289e55ec728f66c36526d40aed7c44e5352fa3e2b5d27bdc1f7b3bfc4f095d002bc8e7b2df2032daea9bd3e0bed2733e7ba13c6d8b45c492becabbef0e8c62c257480faaf6aefede888ce1546046f04ce7e06936cbda44c5f33c71c07b53fb3ac7a72c3770838471aace69f7ea454d2980fb5462173aaade7964b888606741270f92bc18950284d013b692bad63ec3726d8f6596788a032eee6eba08eecc836202b06ca43fcdb062e65f8ec91f7400963bbb8094de4b4e1042e040e81130a3ff037e1b7e947b0c3e8b7ab5b539f8570ec2cf08971a1bf0d4f3e8311b5c625b7d032fe2e0603ac1a4824432b4c7bb5afa190afa30c36f168d196fc89015aff8229fd4b299206e8183897229e95ba06324edc1a9ac81aad786d5c9d38c87c48fc6159934a8532489f06e1aa314a057d91c0f7218861ec241dfccce63d60bfa624652795229fff21cf5ca61ce05ea0ad6a1763173675377d1f2427564e397683428fbcc4b97495735d075f84b289b73389a1fece959f9d9b4932f4d33a0907202c650ccf65327d24cc24922d0d675fb59621e2bcd2eb31ed1ec5fddc96593d4f936ad42731a50f164e404b93920283bf9fa43f8be5878d18e07cb8813914508532625f71d02a615bf47018e3b3efed72d17e0c974d33df18ae70e3a4a743ac3c955d1c366bc94d789c90a8b3771969210c7f0c4b9b12f9fedca5d2b71b6c04e7024a0a5e3d0d3c7d30cf568a659cc17309b223399b39ee5efd0cdec3a34b0735df8d7e0bafa40a8aefd742b3e0157fc89d113af5fcb0cd136a9cfb82acce406054aeda30b83ade59cb6771c366c86b5d9b016a74f220a967b83254db3e24d3e1f5d5e23b21354420dfca6bd8454575c6b0e528744f44ff8e1b9679b084d083b9ebeff09df273a476332dfa172cb98c79ecbf3ae4a722c25be71a3b206d848f56b02fd04bd32ff959e6931bf6cba042f9fde027d832632cc40d4320166f9644296d2dcc845491ed780cb609c0432408d08dddc70fa2d52884df47a55a8a3e706a24ac6ceb375a894636e1ac121040561ef486ffeebed3c5336d123dc05a5cf221b9e895f6d3ffb24b3674039d50fec114d3648d6dcf4fbc0b503658672d675c4ac64dac4db4615d011e82bcabd9573fe9e2af224103f99c4e6e5acd2196f1eb2260fb4aa61c298f99c20c4ac1c9e70a9a9e28db441c3adf2f2b3a00ca3f1398b5a112126fc3f0e2d11d40db36d0aa47d59cf8ece36df8daa854509e546295bebf92465f32a7edae6af4218b49781034329c22ce9e87258216b4a901b823a227e46f2938df336d3fe03fdbff4efc7b8ff8142b4bb60592abe508675d04081e5eee99468971fd49989eddf4648d3948266b08133b8c22ce3523757821a05f254cf5691848b1532655e85fece5eb186e87fea61936fa63aa047b5cb77c08aeeb2991c26619139b951ba4c2ed487703a6123c9c16d7b6cd3a17ad108fbb7c79fc3706f654be77a6cd8a5213d3976b1a4527144e788b712f37cbfd5f4ea5aa5fcc0f91b98d74ddcacb83fb33e039e1b88811a2138743ffc0dc5eae46c688f48c0be92a7094f92c2d4bae0c1dd95d40ffa113f27b8fd1808178be0c3bf77b677df2a17c9443fb6023c9e43c48614fd7d09a904e7a064125f8b7671fbb7529169138feed4bf3e6c215947efbf44fdb5eb1e3a5700fc8bcd1cf2a393ed438e68b607a1061e6cbd46a0b4f30fb4a492e786c5cc6d4f80542d64d0d271308ba072deaf7b1ff1c5200a2bb24c68aa2a5cd07052ee5760e0ede484e0a9c482cc74ab564654787e1698bef1386fe446106af25cf2fd950a78f4b521d547c83b6e4b6ef652444af342e90bb5761ea0dbf6b8c315996ed3d3a1a9d1e5f2b75ab18d0bcc87ee639299a248406d9deefecbb6883fe81ad12f20d80fc9b3c8962cd84e5da215b96bd457d5146aeb36d08185b2ee021fef7fdd43d0555c0fd16e7b66d1f534ae0b87f6299fc3b8e70236f39c91601564a5c2b3abce248ecd5450096e65df8b69de8b5cd963127f207f0df538f990bf29ff0c2930ab8b378b86d2619b150ab4dde82469e40e9e6e503182882b03442f92cf7449fc2c4b2f7ab91cf5ec57b8b0ebc18b27ad9125da57b6c139edbe7495defc73d68e71019b2b808a1ecb89fde58ed9dfce8b17e072170983e3dc3551149108582b95b2e4bd9552faff0bc7fd13dc0130f62a967761928dd62c9cae970ee447842a15baac31a280b443390bfefb7d4115e8d1c6458e1920485985635b75bf84b6af5226dd9b36c94bcd1a5f3ed99fc3e5adc6f3c4a592d2eaecce02d0e5ae97e6c4c7e101d4138e526fe3f090123c6df8596505b4d28c1be9e0c3f83dd4ac07946617eb553f7688f6724c8d8d34281a8c094740170280cec80b61b07eadb2c63427690ce73233f18593537e07266a71523688e6fdd36943d015c1bde058ebf5fcd12ea00e6b41565d062722941ca346b5949078421cf1f32fd38ce1be52fd65f2e9862bc5c57b45f33581eb72d387f0c212d68f85baf7e42058e41966429b0189da4eb4e58c1104e59d34eb0678a1385c38289210ac86f77cf8bef670b9ed04414875a29c27b79bc97e7febc40c39cc8e201ca56b25cd8c96f1a37ff688fd5d55cf5232d216283169b51cb41c631ca323056f2e8f74d5178a83b3bf43cfbbbb40f2680f338a89b294d4b611b0f275cc60e99bfcf270cb761c1c2a6773503a3c0e03b2a858d0cb5f81b032012bb0e15add080cecf19bedadc4d0c7e9d81fa75b97e7ca5e1c9eec88bcd10afc0d8e48f83aee498633bb260d731e0f900a9b0ae3f0d6e57346b8d3d8019bf079b4f909b6dce5718052416de8b61e9ef317ecf2faec5a64c0ce4bd5aa72b79360ff9b58d4a5f3e0648000d90a947933b0fe71d81ebaa96cd4c54c5d42b941e73423efb1f6626767f0e6dde52cd8796b0db31cedaad75b4ff72b83e0e9ece164c4f4dac170cca65e42db8b7ead0fa6e2f0b2d9576c79d1d73aa4b7ce4b954958dea381a65d4287bb1e02b7b88b33b2f1872d92ec6bf79fbb726aa436434fc7f2e79f0ff8bf243e19c08f680a5012c3c3d0501abb46026c420e485166fff457412a075086266b854aeaf5ac3a300ff5501bfd7ae96ebe56a5df5b7abb5fe2d9bf7ffc0005b6aae9dca8380a66c8c14e884f9f3338346ef1daad62042d09cd774b43016b0339aa208c5d6c2f7373797202ffe680edce71d684030e5841edd57cacb0cd596c63515a1b992de113fd2c8330840915d3409f608c8d43601f09dddeda6c504352e4d8ed932d8b5076a557fa2d87914ccbb0fed61c1f4d9950be0900b2212a82abcc4d9f4466d1f5469939c1e35dcd2d124623e2e1aa2dbe90ce09b958edc4e60d7e7502ed16a8a1d08dfcef84eed8a93d6940441ad41e493ec09abce869a8de0a778fd8df55cd1d9d7f053a72314e786088abdf968a3b1d1427e52427c2313182f51b5556466960017468a35ed2594280ad8493a5d3503ee4c479fd4c8db0141fb6b77331b46fb228f94e213ad372c09ebfc8bbcc79eb074162b4694b7bfb8444a4938a80747d2c15879e28e3cf44a8ac2ef8c5d9f93e9cb42f81cf88035dab96b6c078bceb4fb9cf293dc964b0cc9b722a84f3423bd4fbc57ad1f68a8166e86da595fb3b7a4dd87190dc65756e0f7750b68e3f605def2821619fb4fada9cfd1c41343dd4f5424a1911ca709ca745962a484d981db73a23a85c046a3eb73cb48b085d3286738e689437cd4976cbd3e2c5a4a4e385b3aadd09b08859bcf5506164db33d414caee679c65069543f1db8b242dc424af5a45bcb5ef9b939ce94e4dfe56ca25702d7912c38dc44d94670d90109ab685ccd59988bb7e932ffebc882b7dab274065578c041591d481501797b4039da17684a7242622f4390544b05023d3902949d348a44d9ff421d04a55395cb4248c7cb0f13b697f1423be1342d31fa9b85aab70c16bc62eae5660256d2472dc908768ccb7ff02cc84ffc6059752499711338e41d37f8751d84eec47a51c7daaa273e19c523d3d97ee5f71571def6f7268f24afaae71eafc84986eb8a0bdd50fa553c815939a4b56380b22b650fbb989b525d2862c5d420c83509b5c76b1a9a2105b45c2182331ce72e8f1804efee93b1929338f" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a89834aba90aa88f605981578667395d730a408f3d112231997200c28865ec37a4a1f044c8817949cfc8a4fd9028832fc406f1bb919789f2099629c2c8d6bd128de4f51c02a73c49dbf17883ef90a9dbc0350aa67e024a893e555b4f989780a45320af4858421346445f66aa86e4954c0bce0debc896f21bc960e4fbd4eebed93e943a40387210ecda652c36b6da73570500adae1a2a03284dc47f1e370570a07007d8c5a64089e81e2c4d45eb6ca01530884070d46f4605ffe139457d5155c8e9ce8f201715ce7b9d18755304ddb6871551498985ab400cdc4349c6b5ed10b76b425e70376e39a6167622eb5d4b7cbd932aeeaca53b3e28ea06c44780b01684e66eed712559543cea677f1d9807e2ad0312c42e43d399302a0c4c506da7cba37000c3ecd10b58922889e5c8d31a9882ab8ae4b69d3d3d3abc6450881e9b79d9e2ac51085f61ddcfde73e57c1784a6a766ee2bc434d4973a9667c1c8e00eba0c544a36c593a764f519de83251ec48fb45a63e00ad4aaf01d1a295eda0eb95b27fc364c33e3482f4e5256cd622242840d0630194316033219c3051258f548a23c08d205a7b15199ba8d6507565d06a1f20db367183d56d9dcfd2e12c36bed83d239ddbda9efe8507451542942033282bce486f002a215c2ef599a6619247888a81782b005acb3d550988c1f22c6b186038bbfe1c583d165588e00c7f0f7e462a778e6a8126be71ee0dae7b5cfc6145aa6e65c8c136d34f7b45252652995898150cbf177f47ca8d16b583ce5b0e809567c08b99c06095c879b4d945b13c8a62429966347946b606dd78803a3c8113a431761dc868ba2e2541c66b39f30ee21b0ee3fba5eee922d56f49ea60c25a1094381908a69b351e325357d9b4b9e2d040d8be7795891ad46a334a057b0aebdb2e5a9cd760b02c774f60df240e05ed3708840b1663cb435adf9929c138b9fd15461d86328d5895d8778ada79d9b567386a6302b67b4b99a37f4362dea36fa2fa1f1ee2f4f8293b131c57595468aa5ba62a03dfadc9784c8190559887603c4b50b7487e795285705f07f90d8a9ca50e4abf0905d7487f04549132cb3a62ac2e490d6d5bfde20568290b693681218426fa0458eb356b198b41bc60505afdf7219ce5959124e4d1a56ba63345f91ab39afd970997895689c8397c023b1c8b774f92f0e5940c764abe3bd609a0d50a98dfe291e1677e6232896ccdea3d047f35c558daa130ff53cc66aa03ca5687a2006cb7c8c3f", + "slh-dsa-sha2-128s" : "0x3345627b00d77c13ce3ebdd12964c45d0bec4dcaea60b64fbf6cbe7b897b8136" + }, + "pop" : { + "falcon-512" : "0x3906b14ccdbed5db7be47d08798522129164ea6ddb3dbb9b03435bc210adcf65b7be2eee34a0cff0abae6e8bf68b57594952e3a11d308d4a3b2182afc92b4d835f3f14b6009266ca17121aea6fb139793642168f298e631fa26d8be5e945fd77b159e4589311d7a98b2e964f1a158586f37f47cd3567ff0ab62df6c29ad5ded1a3715b8d96c342d0a51db423bb09171f45b2a90a7abef2056a5972af54ad8c4a1f9033c85a2eb17bf392c7867a4bd2b5035944b7a5dca5273e8d3a7a9cfc64c698047b2edec90dbc6f43d471550ecb03ff4e63529d7dae0e72efd9fe4c2fc5d7fd30d77b4679a1eeaa9b035331756f788ffc9489f38c77ef47338d6bbdd84bdc371534d5e60ff5ee2a49f329865e04610f9de2f5f420f51a3e0c84f7a92ff53fd99837278e17f4a66991f2f3a2199776331543d6b7c262c4287c45276c47ab38fa9e2de9a32185731f4ee6b1b6c7f71f7eab59a5a7fb54b92488431ee74a7d10841755548064b8496f12c669bd96499f7a20323973fe61b2ec8be9ae47618d927381a6f6fe57f89a13094c76305cadee5e737b58c41d837724edad2b5fa4250b066acf7e45cfb589d7e2c79d9f2a7cf928dd1a57930f8abdc1d64cc7ce84aa1082b1f1e46cf52a3366c1c3fc158fcb27f3f81cb94ccb6ef6ade8ca8a87f258e77f3308b123a8b11749b1919bed8cb77891e55eb79b41bf1c8a936ab2298a956cffd24ccbad08e8b63508dd1596374ee283c53ab056ff3ba7956ddfcc6f68c5f0b1b1248fe76aa3a346b64ce6e015cc3e2dfd5de75d0870419c8a4c172d964ebb587ddda7a5828f4fd13308bb6315e83d28c86959ed21ad635a07a22095238cb9c9a690d26ddf67e4592342bef6929482d529b037ed542d4cb4374b819b611bf69d3dd52e2a3635053244077d0220", + "slh-dsa-sha2-128s" : "0x6e16547734391aae71d29745c3ac8cdfbc98f07ad9a8470650f0a0085883ff970643e21b67d6ffe83653a99f2198f821cc080dd7f0cd1a612ab5c6f18d9903ecce34a5cdf069ac66483448219c06b131b377900b999f9aecf637713feedd8c7d4beb03ee11bb297bf4a97959ac5c85c0c83b48147fa9a3be7543ce1050394e83ed745f9ec0c555328d3786da46acac3074c0611667ce5bb4613436dac210c2e9cb186e9540d7d43ceed7f278363811083368d50f6d258a46a32c8df5d62c42c4f6f34c627076169428e646774dd6dfe6924e2240e720fd4d53dee8f880c782862de1ff96fc5204a17625ca008781cb120f74caa37a36dd20136567c05e8e7723a2e21569f065d4fc631811ed0b2d9bb67d2c82b755d14cdeaccb797f207427d572419360af1c443fa68064a653b1e21a2b83fbd93e38c7cfa5046eda9b86c68f3855c61cee9f799ba02e611a1b7df6a15fdc4b4b09e51b3b5e1101d0dc90a213cfa0522e93808fc4ab3d8cef74d279b98069988f6b8c6b4c8d3d62fe2a5a62f1a7fb0fbac7fb36529dd85416a330f35a7e4e161757a83282ad50ddf92c6096cc1fe071952dae4540e0c9d0507d4b61ad34751ce17fdf2e99c3e54cb523d3ffe2cbe8a5c58b84bdde119175e9599df6b2666a7721e246305457cdb5ff5ab4ec9d04bca439d59989253bbfd9a91cb10a9fac3134d85ef28f1e4d4f884aa195216a4811be7617c0d6004c7833088851fb7758aa5aac838ddc5e3a51d122fbb03b0b5d0a3593b2b97eb69c2266eb236bcf3134414536ff36895bb081e9fdd2e5a9f9ba71310af0fa126eddac07ca67020ddd4caafc9b102afd4d0bcae2a957bd65d0a15762b98f4dc7b548cfb463ae10ec3da3b5a4e9976b8f05405904aff74d3d536f9a321b41e0c1d5e4fdbbf61b42d868602e112b53552b8199a6618723e5638273822fe0afc5e6fe068d117bd5af3a1c1e8bb4ea9ae639674b51a523d19f950629a76e9a52c673dd153915863d0ad166d357f8d23b85010aa0b8263de0f253a0ec29b0973af15e4419d965164b5fc6a0e782716d86bfeedfabf4d9705e7093373819a814a45b245cdd13a667b31f1724b5cbffe3b5ed3087118715a0ee68af333bf26f1ac1ff13eaad6a5edd5e760e89518e24a982af4f44386c6d92db7a2229bc9c4df23274cb0f10d348f2373a79f55ee05e3dcb256df464c5f3e54ab790f1fdc585cbacd93c775e50bd02f451b69972728bdce813b46742ebe9e45d45c8bc23d4b298b5956e03b48c0a11c602ccdebace8f92e2521cb346f291717cc1881b0fe3a2e23d4ac694b7f1cd296b32b7f2f139877a129079a524d7e3c4f9c36b5836346a6944c49967796d8564ac8d2ed5e804a14c655ef235fcac5a8cf0372f7df952af0de7e4319089b54ea3f718b539a9bca631ea0bcc9259c64626d252245d541d90179364072b081d70da0a7e561ba2902e8ce864a398dccf55a4f583df7b6f6ad6738813eef6815f8f67936b7b842ed7dcc48808156d236d70a9ecba41994bc0296c3a236f2772c747db6ad62723c4457bf8a2e526f306d8012e04adcff0015a88167d199029ecbd333a6ea35f5b9dc2eaf11a14fd09b35bb2842a4e610da4afef15f332d5d1e82c7b0b452b7564eb45193af060595975d496ee59151e3c0f1545ef2694eeb1a2e38d4e008c0943c8e7f181a258c35e5068df39376aad6ef356e0010303d9a0dbcab91dc708b8e9482f50aa18f88f45864d539dc0818148149aa259eeb270a474b0a117de91b6834672a5cb4b5fb7c2ea0ea7e3bc9884163887986aff1cd89c49a6ca417dff067da528dd33dcd1f7db96ed50820bd72dc41984ed5c767478b79d8f61cb7b417893a3a4451129525a915b1e999086e6db74ef627534015206953ed3a5e2bbc5c629b00f3f3b22a37519426c20b2a10c60087ea32fe606d81c6457a02bdcadd8d56360dbd278ced2d4cb9af64af4ec77989475882f3c1c0b9f9e0b6528b9834e34024dbbd64dc19fab35f3bfda66c086f4c0ff06a29e9fe881340d1c0719ef568e3f8f683282b963005e93620c01de327adc0af4ea7f968fa987cb8f38350ebbb3b91192ebb009f9fa85657e06cca6f71edd3d0dbcfafcc11b908cfc38a08ba288910fcb14f97746ac20ff6e6c43eb099d09a717ac03f5e0600f0bb611d6972626999f5661e048476aa999c83e3f476e77e7ae09c6fb7c343a196e1846ef061be75fd8b662932fdd2c7d8784c6e498d4d828c910eca00ac199957fb44d572d4bd2c599fa6c7c2a166d96aaf4959c561d1e9dacf502f76b050c836194fe1897d54a017944a973f49b2ea59b66c24d18f13048e093e83af249854da9fd6b4974b4db3e8e65684b3b6a0a11d942ad5bb667460dd98ab54319cc2ab704a7a57c1583b8289477f5557d2da684aecf66eec3a54da742e2d24a6935ffc4f439d4a8abc21921087df93da2bbdc3a0f61729b397faf12bfe59c85a6af75c9f4ce4af19239840b57c3a4ead6096f2910da23bdfd02ee4f3676897f475d625d0e9a9cc930116bb2327232dacbd0ff32cfaa40aed89024b9ccfacdb76bae77a87b569582442ad2230c8fb3e0dca3937dea5685c46aff9ae6e16438e0e9ea8ba299936a42d68967926edeb3ef955d95c5a0b74a13edd280bdcb462dfc2d82e7ec89377edf6760450e1ebc61f035fff661173de5fc80dc66b8e3d38c67f4ef8c25c2c5626dd2875e10d870f12b3f36f749eaa0c82e94b2f30686cda1da606e0c815a35fe6fd671915483e1b2b48fab000ae560dca0119be7a0a3dfb347ef95c7fcd9a78e76911cb5bd438a7e96562e437e32eb399782a9498ac992716bd9f3d7d4e35a1f1821f9008f547d171ce2b69b007b5e377a3244092396acf958932616bc5cb95059762f48468a94f9f897736b11bec2ce0af9ebaed9add43bf5c262106a96083ad967b7fcb127012b1b37970a6be14cd9088f6ea927a66a76c35e1320452848bf7496fadbc613b2e122e55634f0491397ee5d211d780971998244f303802d7630837d05f3dcc588d8ca32e02f16d2297aa577576ea5c1b90d6d330a18444dba772d7aa94d0bf2000d598a069d03e7873f114cab9174af2f28e257ef2c0e44925da7ccb593d6183fdd37a1abfd7107ce02c655e7966286b72d65394f312d72505803bd53621a08100a041210cb912d5aef260b84e39faa7b5d8a539b6392b32ce6287e1a619804677b3f69ac46fbec010e726f3eb3458433e68e6417ee375dc70c2b2ee16b2cd9b228d6a46f92ba66a8c5b08cbd284dd759478c9f690c5e1e16d6332b5a0b9aeee43ac8ce3eb0c3eb6940b61a65bf7c3348d8dd15302b8b7bf6b6257e074d0af3eda088f7c22e6778c8cfef1f15d9f86c76a4b05c9afa228bc4f116ee8adb959b4ed9afb2307f2252b10baa436795028629d05277ea2722ffc14d9a5fd56e6cd6f0ba218675df025e50586e46429f1c6028e305d22b5479dd045d98a93fa30de08bec1576ea692bfe6b657b4be65578fa2dfcc197212643ec9a1a8de5971c07023abd8767c528025281f9854145e3d9c47e01bd37dc3a35b76639e4716465b862b705ebaf674a64313641de333828d75c16d4f6ebc4193fc2c92e5a3373fc79dcdd8505a8ea7ef8be6ffcecbb2e80017b6fe401e49193ab31e67a56e8163cce767abc393608e7485f41983ce27b9b1de1a0281f237d685d401641f860f8f11d10805fbf0c7125deaf583458787c6f2850b0cc8c62053e069df241192c6af9018158c27e30574a9025af5af73bd956bc008efe84b31fd9a49c110b803947fc939faeb773cd3c33d1b6a96fca0c079f2eeb4e3ff768bbe1cbada9ad22c2d97928389e5f7145a346931e4138d66319f0154cdf3b28cc9bc04139f92e421efb522d0639f02935e228d6eacf3ccf0356b32c163dff6b597d51b64a13c8c93f60f150f445271489db644665ed1208c025b35dcebe82e73034561704c602d1b24e04c08e4ca8fab21c5136b6d4b7af05cf8ed62961b4bc1c2883154116a0ca8933e36042eb98d851be695d8c4de2c2530372c606c93bab254d6b00e59fdaaec98c56415da26ea8449061e12bd22743a1a0e0e1e117a4a9448d75b3a9dfe6f213460ca639632d404af60c764d91405391137ab9ec4e8519d11e3dcae18dfad0404ce14ea4dd36dc3ee24b7a15709410818ed0a42091a01ed9b7f39f7c87d60d2b3dcbba880001a519fb7de152ec06c73d35c08b3e375b9186752931b6108a4b84189fe07a7165e18357a94962dc2761f4eff0df829427646d2210b55915be710e2a1bb413df2055e7aa0b0b9a4067d35881f22c08e61b587a59ef82ed4df267afbcd4c62f22435167f2d7a4a939eaf28a6f1903c8c046b2d7719b7134834cd56964ba860e60ebb6fbf4605a11b52aac2d595cf3b6533fe2452b65dfa0fff71aff39124a88e3e02977d6063a8370f98e3cb8c80f35260a8541b9584b1d2bcb315b394d7641ca56539b4ca8bd9e4fbc594b0a3bf1ed678be2f9a18d31d610b55ee175514738717d47e4821150a799ebce655d0c75228c2160aa1936dd42f9923de2ac97d0dc3ea062d41d5beffdd1d69672b73490d6a5e14a655228bc39ffc8f1e862fd39dc950dfe648e0ff87807e543d02c40411ddfc6c1f8eed733750557c9f68d830c83af737c9d8c3ff0d23987a8e09e6293d707940eef4cc838365775046af35dd26acfbb4264a9ab84818a71c5a13d8e615c5675656671e5fc2e91b682e91eca76ebfeb1c09334e962496f583affefc56dc144873c79d4df77da394845fa29ff348007c8ee7f23f2944c121ee3177879e6938eb043a0abb75991b3e789571e3eff009cf089acee93612e97be85e1cd83da9ff5006c14151afa67fddbd9262667f98df52dfded25f28b36fa32ec662cd5a6b0f844030dbe1c87e9e2d435fca1a81045b8de5b8ccc411a839064bfd133731f55ee2ed2ee727d9382d130ced8a48942ea295bcf75fc74e24204b808956bfc171392f18554764aa80e75a539941d90a4afbce1f981f0c35bcfc78330ecf3d416d0683c7682cbca066a10c43273a0f85495cb4916ec0eee9952431a11717cadc95c7626436953fdd5fbfbf9ffe329159d1bd79499807796d75c458c3e3914d1f73f656e9fa273244615710c3eee84553a7ca0250e97a0b8895695bf86408d2f3cb2fbffffd62c8583af5b25b7e2a38e266c9623429f8d80ab8fdda6774df598f966b86ac228639af66cd0e3ee7ad7714b4ecb42876c31eb2608fab0c6ee26e97fca305aceeee80bbe22cb5e9819322aedff05cdb78b1fb3060ef18ce0385ae0fe6803b414aaf0b340eef94a7517e5cc3df4bd273f92120b8ce73281cab96821ddaab08b41b97708dbc7d0edc49cb362759905ce8d42743f83a0d75878596c4c5ca878b7aae6fee6ca7b20f2ee60ea773499a781b958f4353ceb078e5c04b0fef3e0112dd0e3a7ef19e4c63a202fdcb8ea56d15b2fb0deee4198a19c9aa59c1ef2b3de73f291445c248779eb5fc76953137adce5dff20f2198cc9e729be4adf385e0f25f67f68c04b4cc26987c133b987f477d89977621b8313d6d18a5ff6a76c73ed608f5e4adb860e2e8a6cdf237d4871a98f0c448668bb42f29ccad560fc1e571f8abb3c75665a65a212fe9d2c1c48eae9b92bd0a8b7b3d2fef99ffaf7942f5834533a592423d2cd66aa793e85bc8351876946a1e49e13ee374609932ec1af8d4d3f52e00f783368aa2bba882cc91c5fc6eeb657f3682dca8e9d63f319df3bd1cf52c511f600a6c815e4ad59426d9e1f32831dc5d8b0673c7c58e8bc1601d241bcb76c1306ddd5aea847dc903f376bc4d8e73a55f0a5ad331f57bee70533b29e6b8493d9f11ce4f27f9605f693c05487bd43882ebe8a1090a95abcfc186d74a3708ce3351ce7226183a21c08497dcf52727b7de08ad6a970b2c9f314bc7b33995ffc77a9a36ab46c7a05f61315d18b3f2217703be5753b4ee1670eba3af00e90a94664039aac93e89dcaa59d4566bf286f33c740feb763d0a385855f4410052c52abcfc972298ce03ddcb2e65dca251afbbb75c516ba77001dcaad8470f61e3391c1c68b1b7dfac9e50d53d2ad12876ba96419b39d7f94fd88b58ac1c123790bca733b12dadec6922384c6f3b7284334bed918acc06ecac7674428d56496e9d2e8f0175b3c7e384218380b6d59dc93541ac0a7419eb0ced7ca8c12a487fef9ea50280b7f7067a8855e784362e85703f6b848ddcd4bd31293baecda021fcaa7198b27d37900ac192d71de4e5124089bc1c8234fc455fe71171fc44494395b323fe3575ac720af5cff428326cca56ceb0c47e20e1432018a769f467405f5a8b71e15e0bd32f027640212d893678ec6fa16e5c00bf5c0eaf2de0333126ebba6b2306211cbc731a6baa8fc5a64a30141a8bde237c217161e384af3355b4a253d35766a55cc69be97abead95dc2ccdb4d243f64aa6d74330cbb2fc9fffa79fb6b6baf99e5ba9abd7966efc1586e72bd25f6563fbe125abec67f24e3510bcaa34c49499811ad2425c25593ff362c90c5c4fb3c4c45a4c63c46cae8cb1759ff2d82ac2726b60b4f094b6344191f3828c78d369956b7a28f650f63260c2dcdfa66d1df4731866efa6a335281d1cf2ce3635eb4294c8b2e1d4b7ced7b4b58a10d693e9f5a269b9d3451198735697af3a7b4b630b835e3b56e252f71f9aa6dcdfddfae13668bbbe3538d5aca766dbab6655c4aa906e0106640d21c30f1a241f4f5dedd526e8f579848f1b64808afb54bd2737a18d9a0e4a078705847b59a1ca4b2b554d03304daedca50726cd23326a6507393b2dff27921b5d7d322cb9901e0efce9bfd95039bb3bf6a99282f9dfb1e90289e986a657b3aa212cec6e611423eb3e4523f4723302b1b4ea5db2cc3fe3a7f0f3add38eaf228a6f0c02b9237e4078b438c01a0856d64053427af9f84eb04f022fd12986287b48cde73554b014f75a04d01f7d3fc6285347be643ce214b72c911e6c5360909ec590ffec8b4395fe271e97af852edd3801ccf04d3ed6684c66c67278cf331ac7c9b748e2dff377cafc7fc6732352245aabc9f5f25cc29d8f7f211a88a7aac750e488ad0304b7907bb063e29fccb16dd7ca0b78fc3962046a83f9c7138d49df3b765df47829e5f56e5155dba194d5128648b92e4f5c11bdc9232725faea4b87d6d940b9bd7cead9462736c7ef605336c40c9a053a000ac390b6340dc85a6905d35dc83c2d13786d233952ca8777b5981b761376f4ce2c5bec0587c2b28aecf39371b8899d227dd58c054aea7257dcfd838c32d6053362842a0baee7f967b6a4f5e1cac240896a8ec8fabbc4a551a55e674dc8cede1b9cee3874ff9fd3b91db433a12e78904aa3ec84d57a0169f1719297b467c46b278f671f74b8bb3c2c0cc2a093cf93439c1267e3be38b12e9b5881c27ab47bb01397c6470b80174ede3df34ee51ac116bad6fba58f8b8e2c7e0d1dde184460a5bce9e1d853d11ac017001fccfd5b767afb69691844e8327d39660c06b8916e918f5587f5abcd0f0bc45328ac01b1ace66405ed6c7a5080f5795f2ba0c0d9e5d05f462d89aaa230f240fa47c5c0de0b2099e93ae8c0f94a45f0513ce2797cddccb5216bad88333f6fec5805e0b9474a18ff562e78dfe325c1a6bb3648b41595690cab5ca9805eaa8e13526d036f4233f8181aa8efc76e14031d343bd5d9023fd6cd6b32947e173807883b6a964d398d3fdf9f34a44bc5fe41c5d72ffe5fedaa13fa1da68e19656ed70771529cfe822fc694be1445c79a041eff9753cf47a48ebe9f2c9f1546117b97a1c2bd6e375b957fe38cfcbb29f8eb726f18bc6131b1e27316064980e35d95fd913583b9735c6bfb03ec86e935024e60679a61d972b4aa296189fbc268552acb6cd890c68e50e325719d2f2c99b8105bc92fb582eafc1d618b4e88070e6d8df4924a1780f63ee216c0e937073725353b0c97acae72ebe521fb05fa78a4d6ecb7ba2b860190b6a5c340c83249919c26466c9cf4a9b774fc823141f65196df32edddad59fb05ba24738d81734004c42a7f92f5234b7f4fc0d605b032b08eda4cdfa3b03a3b805ecf5e4ad667a8cf399efb3c1d6822d9d5bb5eece6e08a515cd75c4485537eed6cbd55efb0a3d393d2cdcf44846f0c27cf6a664c70dbd54fd56b20bcc482de2eba84163375b1bccd43da63467557828b791dfb0a976628206e26e9eb10f1e9b9fc34571cc1a38f54909a1ee0b097e0f22f3ca58b56c631708e9baa52d7eb551bd327a6e0014fe98f04d7b0544fadc06c16a5652ea16b6c92a014bdeb2c070843152847293a0a925cf3c22bc9ac23464919ba6340cf233990690c94601876bc30bd5283f97cfdee757caee7b7b41fc265fb2ca9751e3b523e8498d665151d1c67624a084571f6c29973e624703d2e87f1ae5a5e0dbeeb96f46698223e063d10d85fd13fa0deb3891359844ad5e8100036d9b70b3b7831ae78c927a5e2c2ec62992cc424894ccd6f064e9ac307e6a2dd092deed179cf60b4bdc6c56c32a538d477d7917f7203f898659104ad5aab3e520a9f21d9c01b581cc968a68a77648cd95f66b5a1eb1b9d60b0a73c2eb05873f279c4ce43409853df2c8db254fd5b63af900c764ad4a0aeb4641e1a4e4089967e83603cef0317d06d1b990354c11b17c83c2a78cb9152615b9eed33e20f61a83179748ed4044a65edf285b155d41c735a883a481fa07c4941865b86d3cbd54c345d7933e32daa90a1291211191e0a3a5015f0f60d2ff07782606c164485aa469bc97f74dbf1345ea631bacb972be00547074759fab2b98a78cc43b7e4fee587e40e23d9f8ecaf03337d47d27bce633ccd97c0b18509afe35d54b1761a4ecbff6c44dba5402f3111eb671183b5e6bdcd69398056b980c35628f03214e0cb4ee5364e51de27843ad6c24b1e972889d54ed2a31d74000c32fc90b3c96dc3761c9649305c5f5e75f5b5ab7eaee213dbdda1039d4f3f450c77e6434752b74a55c42c98719836fd91fceb27be449d65c89a823218e882b987277622ce62fd0b46130760f10be6b49ec57e25cf6584a99433cedb1ecb1ce25cbf5e2b6eb716c12064f2cc45f4989c4c1b6c1ebd390c44195a16da16340d3a8588754ae676a990df1eaf04336b3252a5177ca3b3b2afd8e7555e92be6f6d7e64bea52c8d25adce3c580b5731574729107afe0e1025e6da2f2b05d6cc223778ac7de1202566098d43d0e4db6df8d242b57353cbadc0711030c6612cb92a9f9685c88856a0739e61d08c8fc5864fb9d8f41b27b43152ec15b3307e2c6f20e00254984e6284763acc977d9e16ffd4ca8be3ba7a289e3c5863371c132b8300f59d50f9efd344d1cf51c5ac64f29210740be2dfb720397941e2ba939e31c9ed563a674628b6ec3c8983be01e41eca61f869b920bf08db3255cc04a1f459653a4a3c204e6ec5496af1984f5e47caf56993bce1124bbfdb5d7081895a96170241307f000ed22ba92d5f5d920ab39d3ad1e288abb76ec3acbda6a7779be4c65e99bc7d7d5aa57d08ca1774e0fb146cd3a894beac12715bf322febeb256518addf26df507db3c79cb2883e6bb1e4716e185026cd760c76038d52a2e0695d76b3bf9da7df9f4dea5323f7dddc8ea1a0031851928e354e703651f7a21df505a4e7f9f7a8a398582aaa9f504510c0bc6f9af8942601bb3a6df3faf2e97c9df28268e1dd466857d413fa0bab4ff017f12554dd317455d6c716f9449c5273ffc959f147d7e0da0c2eab432b83f96f7d2de7b1ebc41eaf2128f8eb8f9c09c7b3fb4e95bb3424b216232406b81abd4c0afe11835a8425304376b08a4442c6eb0e88a51bc67a49b143bb35416c9ce613538483c091bf181d322efe10b2204aa0f0e01507a7cb809cb54b5cca5f44c20da32b2b7ec52098e53de672795b68a44ba667bccac558210307ac27bc5c203a2e6a264e7cf3028cb4ed41e29bc46985c1440187155b8bd737fbf9480749d62481d805463d75ec17c7ba83c60cc7020fe4b110ab1a248f6fbfda3e5b8a688a280f87a53ea7154311951c2001a7894eee171a64cba9b19a21e09540dc5ba85b1b2880efc39c8886dee72e64f045d00d99a90ffc81fc957ee65b7bb93e113717e7e7a275814404198f9e9d33f45335908f1ae917e0322b3d12c3d298ee814d22f8a57304faa93c18a14fdf53f05499239b66498512a6856ea194705521857a1724bc2925ba34f0e2707c84ee234c3436b4d3ab46cab863a28a011236e564ddbab3d49baf77be49ff2f5f2ca75252971a421aa2d4975aeacedba6cd3629efef8b2322b516c60ecca2541112b59d73b88177cd3be06163a463306cac2c0c1b11f84ab9b2fb6ab776bf0dc0fcb381975e4c1a0198b41bf6c1587bf6ca7272d8c722a581bce4494be5431cc5e3038ec33d843b9049b018fffa03cb3ff58e5da6bd6d49ffa6f776a5c4fee3cb258c4bf92e5e4079e38437f321e0c29d608cf5f69129e705e66a7036d67d43fd4a2919271fe07052fd30c049b793d2af0dec2431ea687077cb7a7a5badc21fcb0c2ac10bb37247207596d33d1c2232fc2a225acb9f30be7fe6da19514def9b595929c1c0c4b146099bcd980356b601ed0701299ca1a5547d62badd0475bc76fe51d0565b95b52a801f14e1f1ca62526cf8931f0706e3a478d20ce7eb1b1088454beac15277a72c90dd95f6146f961e41caccb2cddb2cd0680760731bb6db221944eab186425b0c2caedd840be5a2575d8dfd5810c4db5403d4491304130bbc564cb578d814d952706570e56641e8ce6232a2d4b18941c35301cd5c29211237c274dfb1aae379e25efdf827ff6e41cade1d57ed229a6ba76bf98de6592e5ae3ffcd481b53071e452a0b25f12919003474bf4ad2f36138d3d7c23c89b5b41a25c3622bf1dab6762cfa86a5bdeb6f2baa9cb3ea7dfdf9bdc700f4062ce8fdf477a17088bb2bf654e2eb9ab943472dadd73e3b63a4fdcf90fffa98983130e16d512b54aa0d4acbb1604d00fb2adc4a88e87d466237ee7b0fb33e5f384656a8759143db4c377e2cd264259" + }, + "rotation" : { + "falcon-512" : "0x398c95150c8779a927aff8c6d9e3ec76e88a29b75d92eebc295296c2ee884f0a2379fa05bf5070639631133c9f52157b6972ecea3a9fad349a3417b92bc1b9eeb32505801a35cbbcb6546db5f67672940b1ea9fb8ab5fe58a3c9242b677355db84d2c9e452c4ccbd3dc4d95b2329b5631a429f4aa10a772f15bdba2bfc92a1b0d6eb02b4c70b45ab224b2c69d6f1ed771489a2c9e29356e63e3feca0c4a8d9bfe5f7c709385eb1225d93197e054b263e911c8132077a4564f1182a4691de144401f66b162fce3b844d67710d41ecc92398f3169df8fae4c76dd370355ccbfcbda290e1fa8c29685a7e077f58c9b675faadc3b9cdcd7df1ae9d6b209c4128f3492260f9230416739832c91a429ee2a1b1133699b1a427299d54e0917bd2047ad0061ba9c982d614350ecb0b6ae8b914f911a1b603344f954bd775f2f2311bb851ac8044f4eedf2a44dc4b1a26adb0d8ba9c7c344b4157873e72ba1a2d84e7d07dd99d05127bdb6f5d011e4bfab388ceddab572553f8363bf0d157b17a939bc96979239da528150b79a0c214de36805f170c54e9fbc52ee43770c2ea4d3dfe89b7e1cbe3bd0b3f7627db43e3fdfae3ecd1486590e609441166e24f2c814bb2487ae8e74a2d882e0daa657246ea5f706f60384edf767ec6465855328f3cc1b51e8669225a1cfdc21b9a52b6a661cbaa47e78c94bd0921c9474772e0c2abc9b68b9b3f848edbd2bcf9a531559a532adf16668220a55530d0c74ef93e52f3b2536595aea5362bc6fa14b0140487749722d3afb4bd26db204c62f115e01178626ad2426b7c058d0d419957461d3b9973b12fdf21e66d5cfbbed2c4c8e484ada7bc43cc023c2c99942314919a5f3a868c36ec3b9d8e6278f037966c8a39d32aaf0ec131b9350bfdeb8cba218387389b61d0", + "slh-dsa-sha2-128s" : "0x9ad59fc071d803b9023fd65b499d1f93f09fcbf693eaab05f864ad85c31d2b072baf524898af2096e711c65dd070168833d9f1565d4636f5a9c2e65d3ed87740da30981e2939749d5f4da0c91ca6520101a99d56b9215d3e2edd1e91df5ae968c04dfcc508aa70d16035ea7a7a7369c5f5e1b7edff39db94ea13ad4267802f87c11cee817f3f566d62e2bb68d43fe8ad9f740529b23aa944b73e38cc2b1d76337c9d7c020189e0982ee60d1936898f36251de7a94a89b8bf89ff45bf6c54dbf8651947cb181eddb6a2c1cdc481e552422225639534ad12345462c7d282324ec00e012515d2c2a382edef86fa87399de982cda3f59fb1cef212c09301d6eb10b6f5491d9442dbd70b362944a961ed2b0f588cbeafca796078f986a46dfc99636a31154f10c4721198c22f2ce35ab7911bb1f86fe08f4d4977fd0ba9e33efbfd8429fd2e2dece6ce1d1f798f7b3c501ca903bd248f977bbef2567c562d9f4bfe3ad522a1494a0439f10237e2758286b58a245cad505c662433a427b6c14883abe8be61aed798c4564c6c24dc072ef9d6b9ccf3d7aee26cc4e8bac1fb639f9af1ac5ecdfc6a412cca0a1fb5759230171d45886c0585d3634295bde2a2055df5c8949c2a52aade0785696c7c6fbda2db9217e71caa834c55f51646102e64d0f14ee16a18ab03d2b0c8540ad05a3b80bab5755c23a654dc60758e8130952bb1b1306e2e92ea15c9feb58bef481ed6e9d7335503577d27bf22274c7a079badb6117c85a06587016d1bbecf53aba658649554b67b2cd8dd4b94b519477f11db77753ba1408a62215fad93cda89498d14b387ded2a142bf5c6e85f2a658ee295a57861f9a408529fc5e83006a551a0f30c0996d8a34b6932aeb7e9c102ddc9e0559af6003a361ef9a9214450adaba2d2a04e11f27ee7e1a495c87da925f50d5afccd6eae686c100e817663aaf5b6a9bef72c928dc34de62408bcd72f791d398ffe61a2c6dc865bc28c324a2e060f2c7e2f8e9c6b913d6bdca590e81448e2d7317a661cb83393648814a2ed1d54ca14d1930d1d8d20a169f8a614393fc949063917ed099d2f6a7673df30ee8aa8cd5614069e4c612f4734b06130ca1351263249621db9f612dda4b853d9f9224ba624d8f716fe5e9eaecade48463d6e6da0f639f895c1e5f8e9337bb0e98a6347022315f729edd1e252ec9663030acd0fca1b7524391c3113792d3dd60e81b5b55fb58072db55dffe1dcfe8d531b647b5c64182cdf7794773d2ba27b7bb34433337e14e1c114daff27121a0feff0558254262f4073d9d692e662ca7773655ccfc80bf3b80fac6b507aac4c2a40345bd3e7598e2ab6eee8da230374b46ee6e40ada0e750d9c345e153f0a66cdf30cce8e2b9eabb46132db6ae6f45f87d0fa6ad092abc21594656688824da4536b5be57c3c0d7cab3bf21a268bdf0e1f7cdbf1e774d374bcdf9787f08631437e7d711d218db3c64a43fb5943bd114ac5a673082e8a687702cfe0d5ca620c994e0ee1fa76248972060f45ce79f25c070c1d147b9301c05a6cdfd385e0f5ba2ae78ddf2303dbe5cbaa2188597bf13e7ebab864d447cf06112dc1b38d1e9abfe82fc750585eb51b79f95176b82322e13a57cc6948a28728e98f7061b2a05967a26d19f81adc54ea613376495ea2cfc0c23b6f107c3506b33b367223dc88d5cc34342efecf9a7d66e9426c7e744f20cd89e26dc88561eceb65a16ceb6e330c82c9bd4b4d30cb5db50ad4ae0b2fd751ddb794dd434717574d4d485b29e9c767f8aa5c0277884f1de828e5e213bc05e75467f7b57dd36afb3d5ae2b48683d0b5dac05e6f104adf3afcc8d334c90718340a4c9ae880499e0534dc8ba48f605a38564275fc52574e0faa554a52c23f84853623c54db93acdc1c32a952593e5cd75e17e2619c48afd27c3cd8a3be62956babc6e2795565a48e21475a4679aeb9bdd13f5b304448a16e307b28791329173847d7ce6c0990b4a1b1a8278ad6671cb5f83af5bd6ecec387a434917f2a902fd36f7c79d898478a40076ee87f6ce03c6ce5b0bca864fe58a4a61ad8727ff11b16c8a28f7208b2ad4f2c30e79fea01df53584c40e8f3ddb66c3e4b25b7781c8eb144b55865bf2d70c8b8f1dd7778075ee906bca43b1d5092ccb811754c612df33566cab5c7e2b1e04670ebe50b2a4fdf80aa1ecabcc4366a21da389c0cc6ba252b587c714e98ee9e832c56bafbb9c0365d5a05c026db5ee72281ea64ef43c7da8c289ef3bdc82a78b7c1b352095e086d9b246e96d3b54677eb7af15121b4b4b59a00d5e247c9b22c98226565b19d393b5a412cfbb8515fc77f4e137fe2bd93c0ec3bc61f8c180ce57f45b9d1825df36148b2572a10ab550cf22bcac64d8605be4a0d18683a65a6720a9d765007709157f218d3b751f3cbb2e8733e3c05ab6938d568573bf65ccfdd682dde65c25bb288786ecbccd4d7dfa998267a4b4e6f9629b75909ac731c5a6530feb97f38e25961672ac6a8b2b3c39feed6bda2872ec2d96bc4bd708b6bf05ec6e38ea2e29053584ae53dc12668cbe21a851d225e773c8e9efe0c30a183d158771eb255df45ae91ed5d37daea1b3708f22ca2522aca6ea0c6269a16fedcd3a1dc9dfa8d4333c7dcf95e0c8a983d113423c5a4ba55428fa016bb15e1a42004e7d88df137a77914f322b456f9b2c9f537e75ed7bd6e25d4f6c51a2af8d45915f245892bad2694a56f4cadfff818e211c3853165d51d6cd91f129d85b14b067b9eaebbd3d3a3f849d0d45f51a690c4dcc9b4766f18ea30ddc541ad8fd97f7e4214640106b542db8fe31519d1c86d1e7a694aec2dd4a3cc97357e24c8f04f815a4bfe7c016d18149694d00a930a4f2ff889902a58d59cc7af6893ca538e1141a1f602bd6692e347dec834bd12dffb019490166a2aedd25daae3cdef372d8bd3ad02ae3ce4d96f22913a89534b6eb428b0ed3794f477ca5bcad0d1bd8c55e50444295041f45ba07f1f745a085aac47cdee14f7a4f0517a2644ca3c66dc05caeda11a3dc500d94326984078bcd9369c77b51cf3e7fb52f186b44f23b48ed6b12a24232d3909383c49f377f29d5650d6545802af24181c580e4b10f4f879146c6849973e36e00faf7cea302b0f86465fe0cbd5fa93e6f46e3660e9ddfd1ff0691d94d20005701a378a34c150fe5a1b33f6b484745c753b8b95d1e6424ebe566fccf7df7c3318482872f352f66fccbc3f8847796ff923fc59a129de107e251bf8bf00c09e7419ccd18ac92c26e20952c68c22706794a66b934021c031d98c8fc80447155455c6c7078da4ad9df937a61b09fffacda649e71be02590c8ac4cefb9a486cfc25678fb8bdac17a5badbb7f76a740cceaeb03e30ce058b5fdf707b60be946893ba67ab9d38da4640c928b071c36fca0b9c2176f7cbdcb8a595b04b1aa41f5339f64928e89ffbb3137e6ad9d14d31e2647b0ef1d92ce35d4cbf18e1d586433f310e5fbcac2a6737fa06d2469e3860d994816f54c19b9b3ee2b86b477537d8f9e285a4fd168ffc304ab2cb39e0e46ce105f01354c307cc670e152931fa4f24b0b10a4faf2d862d982e3ac625be859e4ae03a697dd52fa57ea20f72da895bfb16ddf8e666f3a8d2733775a5f9313ced51c7698110e26be215e9557f7824918b9d15fd690915e06299778e80bb29f82a75505a04d71ed5d84a1e9bff12ad98cceb9104232932d68d579cc8440bd1c2f005773acce5a2fcfcad20734927434b7f3e0aa7b49205d4a619e8480bdd94ca8c89cb729a6cd7cfe9bcbe7c13d831a4b0374c3bd7b1520abe3a7327d54f07f387900cf3f6a3be59550da22c9279d44499094194c45d57f666032a41f967737d75122ca5d0b06de9e996da34a86e51dd5b45cff00ea0d88082ac6e3d4ba526b0eedc50c0d7c781db3c6e5fe1f302f3ce7aecab3f6ea9006ad36bf991b2b2955c72bda72e7afcd06e2308b2bac2ff2d55e1cc20641ff5e3a70a2d20a97799b6b1da1fb72b5b9fc7e845d89afb64ea0857c61034d20af677b679c054f4153d9115a41ebc96365f61b0e8ddba55eacb0c5be73d56b00001b22f1c9fb39a3d655117eec07a7c87922cba12d5667bd4eccfac5f53e521023300aa936baac8ca5b243d1d68498eb552a79793bfd7d90f929be7f0e636a3f13ea18395ddbb879d98b7263b17f32fe7d046ecb6032d9f1ac44de5db1b73227822c12a1726158090ff8cdc45af9491eaddf269c06acc3ec727a7d17bd1505dc593f62405eafa963f93ec3d827efc9f97106750ad4f95f5c44958225df037144a1d65aaba1d31fdb8b7e386d4a89bb74d3ca3ca26972b45480fd9db7fd3ab7fae2e6e32ce32fb7b0d9a145b86982ada12646142a99adc42d5bdca59191de5ee33c40dd90f8a03296cfe632df8b12747be95ce0da70556c4cc4a68b794b50944c4ed12027ecacce071f33114ee7d0cc188bb0600ab2e334cded47e2691940343c842da0c954ff3cd79d477a0a734daf8586121eb004e7df29693b4f583f69be26f7074efd49c6a826a5454bc492f145a364233d86ef517202f75dedbafd42e9315ca81896b89ece648a300548adb71c3abbc6ea15cd0b493f38d848de4d26fc8ff9ac6ac6530b6d2a25c29135cb45e0714f8b47b36dd39c9b329c6d463b443e3407f006e73e72791225555fc693a0a44c52c04cad45da569375b2651a1a8398f746486412320e2ed9b621c490fdab93cc1f6eb2f994bc4c65b1a078d8e9bf56da9276add18d29f1a7f01416fdccef7f8e755fd759d7b73acef4f09bfe96bf7510e0e2d878609e38d1b70487c10c3b29302a139fac460fbca5d9e194754e995410b28f9aa109243e7808dd54a9ac24183d0a4978dc7932904595ffea85a34c1c4b169a1000c697a5a4b51c4b40b26fd145c5f15f8f7505ec75305a683ac860e945da9d596ffc573aa985232be6a0becbe936a6184271e434379d383ee1bd406e7e9fb566beeda09900caf84917330170a4f046b9f1f1e597bd4d070f91cbe641eea8caa07fdeb58fc2a6d77c692266048af382b76a2db232f53688fec33f322a2d205fa05c669a65f736bf2e53cc9e23bec381e894c4739496949f6ddbddb6fd7212f80ca5347edabfaedced56b64b8761d0c522921ae8e2ec18120fd41f2f00f97d5f87971188188f5264f05ba903a3e13a95376b30e4adfc43f56d1f49914ea03eab16608db50637ac62af4bd83ad20c02f1685336e70e758dfd4f2009eadaccab91550d8eb23db8131f9e05ab0cc61b4d565e5bfee741d557e77b4cb91e76c73007d77b671c6f6b749d4ac8a5217270f7b02e44daae56f3c198518aa199c89eab9807d30c20d95582b35f649f5c678d9e76af4ef19f4b5a10cd6624622ac427fc78f91b9ea01a15e6f61cea48f97fbd8b42f1a3cebbc27b1122eecc5209dd5a0cffbbf96d663667bb101dcaea40dacada24d3a684dbafc8d1096de701a660202e75c8d3049280c2924bb1bd2817b15416f7ef1c69829ba22ba014536e3d577dcd6500ea79da60ff8fff0d9bed956665a0a2ea693b603ea348d89ba50e237da560e954b40192d9ab0df63f604bb343eb7b265d5e1b2ad243dac135cd0be9b82ad4b3b11f953fee40166116ffaa168a7f8ad00ec1bb865d067dedef1a3ab8f176013697e03324f5a747ce17c3e516b13d3aca137046ae7999330699bb721d76a279778ac99485684cc922879920cb69fbb139b9de824857526149e77248f58ac8862b8221245efe6acb7ee8c1fd12b8ecbac3619230f4cef3d3867e9d504008a3298fdfce8e37d34a4e752b5a63692253cdd2859e50e1f2a8d73567131c98be69c2495a593b211532b6d471c78d76a4dab957aa8a88b5cbe0f2ffa5f87fc7382f7371719d8e538360c9048236966f19eef79ac680f1583e55618ddd439fffbefce16fc8159343a544545cdf786d861af25891447e38e6b301e6c787c15a7f6b253dbeae713df279aaf6c8f968c59417e8bed6b09f25f5611db5297c188aa2486af8d3ff1d59ef71a5ec8358e4ab428bb8e4911e746c498f302786010599d8cd773a7cae5ff701388e85c0f52dd6fc80da5eaab12a0cb051b2b8c9e251188b0c83e0aa8bf38fa1363e7a8c527b8c8cbc5142c3143e3b22528e032bd026589294efce133e0e62088044a602554c0c9f514c67a9c43db4cce50e4905c31ba935d811fe251874e92e7e9b0e4ca65f1fe3761f490416eeff598a25b2ca2528ff2fb7dba6929c07e6a567e1583fb634f293bd02ee99f7eaab121c85f546c74638f4bf82b90c27468d96439d1aeedd57e462a05a284d8058fc36b3afb73e47b51f5fe4098ab2697a1217d4be00c09bca71373334c26292d95565388f57d83757273136f3414cfcb4aa2407013381670e7bce0b0178b16f45a7962291a61bd2573caeed2005eecb0f158f2fc6b4797c2a2bc1a50d194f6506f516af12165fd617bd1ce9f1501591f99f9dcc74dec679263ab2ae63ab26233c32b2f10d39817f7be317dba040b83a86ebcb426f4e9fe351003a0e3d0ba58bfc502880c939bfebd20674c9ad7fbb80deef53ebc64f8596db14ffc92c4021749f17b35b34bfd047537005af6c21dbe8a85bd0a7f36b06f09a72359253bb2644f65e737017294530fdb6e6d7306f0b5e2e629c0ad9d2a4aa1a545ba90019fd7a3688ff2718d7815b215c6ce2404af3f89df3fa4b18696114c1ec9e2f00507cc1f240726152d36f8a8d2574bdc62bb1c1bb57ae33f46931ba71b0798aabdda2886dbe530047e9ce8a0570681f60e56bba402d9cf81a0b84ae3c3955dba65d9d947170647a4ff9f1e27594db53892d322db03441c693467cd89eefea6a31fba70d7bf77e59aa817805f953a0f371b81cbbda07782809e9704cc285d855da4084f3d3a9050b9d3371a399c9d59aeedbd979c611835739ad3088837174e2b60945887657688befe3f2cd829c1ed92dae43b877d96461230be1d57e31bcac5873415059bc46e78a6523ba40c6f8dc597a2de4884d7d10b30498e1904657e1baa6c6e47e0733fff28ecb845668469144caf18d17dd11eb09bcebc197bddfbf1f328c1888483206be387a4fb87512925543860e290284310047bdcd59ffe59317cf288e7784659c8e75d2dc55f8f561a5f3eceb7c1a691eec680a080a12b9ec491b3d96f61a6a17190033c580530afdb60559c197e58a2d769dc96c72744a7a5a92e65178cdb6ef888c1317a1f1c70f70476ec01d5a47af5918bb19769e7630b471d35bca21daa34242123eb013f770f9db4c0f4fefb38ca191ac9dff2e873dca9aebcc041728c9a9d502e96069d7387c88e7be143818b829cae60c21a7bab3e8d3f632af12e194ecfcf7a5967508ea496bc8246d4e3ba9fba8d4c7fbaeffd494aae82d73ee69f0b29ca537a18910dccf901292612436207d0d4161699fadfae013572537d9ecc6cb36da4f6ce91d7c67b3d5612af1c33259e06855d66df8da02d23e885b501f0756187da39fa61a3f50904152a7e34817a5b870630e0595824fb3e5065ca0c7863cf52418e5860cb3441650ebd4b3aae2cfc83964e1852d46be856b416ea7b31bb4aac92c1e011692d8b65b6bb42f3596d8989b1d1063e6b7c0e7eaf6cbef5139e1b78c19a042960d548abbe4faaf664cec2ee73ca30a021318e52689618a725998dd6187415bb071bc1126c4e74fa0b7d8815b61d0f9f63dadc9bd55695df4ca451bd8fecf29232d118596e4d8929a855a76e40871678e62e380b38fd9c11534946d831e398276de1f5dba90ef4c18e7cdf2ac5bcbcd2f5289c26c2054e3bb2cea1abf327ec163623917cf6448cb9f7d9e3a8312cab0afd383041b02a25107060d2ae266ff8d9c83746f2e11673e568986f081225f70d1be8b2d78030a2f5b51ac846bbce6d726615dfc56f355e5ed96e4dff5d56c4a564f9a9e1ebd1239bbcff471fba52bc4ea7e3f6d481017948da456eb18dcfb23fa9eab669b12ed6ceb800683eb5330b7e7e32010612ed8c471c594337fb9ac068268eff3eadc2f35c21082d81ca0cb5a60d590661ae8778dfce0271fa2dfa34bce8323da42bcd3d907dbb985a4d5fb889b905ed82e442c7d5e518b4b3f095b45252f7251d92c8c26a5ecf63eaf7a9a0828a9b830e332b736fa30e816b3430228524bf4723c733f75f5b959c50d69f358c8244c806efed3514a1ddb172ce4706c140ba00e94621e05e235d6354a305614dc0f2fc3a69d71c40619e75dfdf875e4375d20ac09bb6ef1bb781bf06bceebddce17ca9c81718fa48ee71a37c99d57d3f94cb4c7bf4cd56ccd90f23937bd978dffcc19f6ba92a0e86acf730edcb9bfe48cd1a6e205fd85e4e534a78f1a41007194f28fadc4bce7d573750ee311953e1af1eae9249c92c2cbde01a341222b5f9fcc5f61c313a45d52271c67bc980c3ab696e4fc6cc6ea1c20d8b21d789391b83747bfae0b61a319e20017433fba9289fb0fbeb661592d7c95c4ea490cab3a72eb535ff4f8ddc5d87cf449315768d7c193d296992a42d93f8916f9bd6da396119d12aeac64b06e60ff1188b7023cdc262b4db2e38af9b20bd58162da310ae8a274186ca7a49bff22340eecfac62c2f7b04180be8917b08d9bb8b00cd9d4523ad209abf6340f905b6e6b6f93a5c89132966216445db84e8d6b0f7c749e7063397e5714cbfac29c4e1af8865906efe738a943e8db7c07fe9f1fbad804297bb916e17e79a67377723fee95384b2c858426b352ad7a72305987721b2017c59e2f9592a8d6e433e58158c562042016e7302f6085b1f9efd07f2e8bc3ec588552784fb8226f9235ba94d1b6d7d759be7a2e43952511c17a4d453e4be4547e91c2ece086a8eff9d8ef36cbb4efb06c872ea929d60d438ba419cedaa0445fc2fa188cb4d77be5b294c3327d2ff5e94a2fd212dea59ba023898390ed118bfeda2470d4e217bd91e9e5464b974aaa3cf9fa7a3d9426c1e034a8a37dea325880a5b94facc17156008afb72d22e920bb96e022b67462510898b238f5914900e863800873a1de639bee3884f17dd3c04b62296088323d21db99163c3e7fca9e72affc5678e808c788651b00ad65db3e58e7c557f05c0bafdc3f9c7986f4cd6062dd76f08048c79538a8f4dad7d95578f6b343860628cffbac2b938f4d16da5cd3306cf01f233ea30ade11055b6c380d3f15de84832d2d84bc7483d52477abf14da42362deae1fd3ef4328b677bd434faa9a05ffb873ce0574d368c2c87af5a3973cca38f8caee04545c323886e695a1e1619197f79ac047c858ba33a2cfa23297d5c28e4d6b78bc2c8f3202a666578fdea43d395f9ca100e481d374d479e908f463a7c6b8367b7c7e4fd616df811afd2c09b4af69e49090c2fb4c1e8958d12eaad117b7d1243344471ae5cc4672060239172e3cb314de024b35c78a45b4058f7082cc978eaf68ccdce9f53d96d7b3251774fbe5a01eef1b5586e78183b74ff6924e0b1d532ef1a0478073d1a6b29a6bc5554a70770240d715a5979f34585e24aef0ffebbc4e4b15f713e439aef62e18b9cb0d9065192841bfdc36fe7f5ecf521e75268ef148ef16e6f9ddbe0d4ef25fb63249ddb21ebf3eacb595b5e56ef6356de198643aa12c6810548a4ea12e54fcce7a90e0f37f6357acb90fdf3ba928845f573604518cf5ab5b5111f9ec0ca12a5b9aa50429e23473c3ac0afef7704c8b57111bd07f2aa1d9a71613fb2f300de45291c68b848c254c0d7f92b5851b6a4333fe09f3bcf663577bf9219cbcd02147d8171562544bbca85feae9ed654ef6dcfb08cc6714c30c5600f3079a743307bc68f3ade52e53f8fcebd7c99bf8bc8db6757ea852c19b6a3bac2e33271ed49ec3671793022c7c8e16b6e80a428cd3404a26c409878571d51eff1c93b2e7bc2c692dadd9138e3ec076e4c86c56f44587c7dc2192811804f3ce2a63bca61564fbcb0f5ce680e5b085aaad8c1fd2fdf849a15323f17745becf2bf84c7d3525e2c684c078c92c83110f3db3a43b1b8f3d5d9d58f7a3862a6e1ab6293b816bd539c00eafbbf8668d95086c0abcddda726ce1e0ba43bed741d3b3492524228bf217f4ed608cd8aff731e93985d8c7c3229c702cd3b423225111db63c566f0daa63af7225e5d54aa2e449fb691cc45729079a6dc4a712be0c88ded413f70e93dd912a0557eeefd3762dadd48b4ddbfa48ed122930e515a852c1586c9a7db6ca2a21646d64537e7987548247fc0e819891638177ceb44e2fd2cb548a5f298630f744f02973b30cb965c174ba3cd0b13871a8a7e4ccea85501fdb04e7b3afce1e5f0970ce7d1bbf51bd0281337fe5d56d39ac8df51e134a0421635431802704515586861ab1ef9f0e555c4c786b5562dbae90792f4c42299f100a447a627efba8dd8272b9159b4a7eae573e708138640197f044863f0a1f64b9c011a6f805e1a055b5cb65aaf329bb9b18213ead439930dabd278f87523e4726555560ba195549d4a0020ee18dac609ad4a9e8986a9a9e487b70665aa2f0503776343ba30e0e71631fd716f2df46aeff73c768b3b1c4cd838ac365c46f02ac3ce04220c3016ffbbff234bfa00f25553912464a8a5aa6349597373060ceeeb56f8a66c14f8dea8219bf03044b955249b975b948b42aeb0fc97e571965a447b4204948250c230f8956583e5f5057b0374ac30acf6df8c2cdee0c154b4eb1f51e9e2b579901730fec315ff9ff344f913ba9cc1bebcb0d696217a6f7a1d0c14c23ac2d114a356efc62ad2c48ff456fbdacd15a06180bb2a55c50523fea3f447d11f4319b0e9daec9cb0df121c331bfd05ae7676ce6f8966b855c6c83d016b6517ffcb24be26f1283cd3cb2e8cc4c8b32126dd4b0dd50b63474f6b4cec5de2d05278b25408998e07ef6d74def57f6500beb2c87c2767e1ffb7f9b3d36280d0f0678e9313dae35c3943cb2d69b5bbfb4092dbfdbdc55d7d4ebefb20dc073e6efdfb23fd2d4b9bff2551d98587af13867fb17fdb1e77d95b286539260631bc800bd27f3cf3f6904d81d4cb8a19a924af0dc45253d370c5a1e7dce7a2d1457dbf59eba5e4a17545d1c86acaa4805e4aad" + } + }, + "2" : { + "id" : "0x41f547a71d1404f147606491f3b96e49f67d4658", + "keys" : { + "falcon-512" : "0x09b3f618864123704b78a9552a0b068f395444870c2cbd5d741a0c565554bc2785a691ff502d6ed0e14c925eaa24c075d20949d12f369848e178d50b085e87c9b251f68b61ec506435bf5b53a49d299409f54e906daed0c1a25a63563090f6660380321e786311b492bd7ff684a2650bec34d5bdf071c49f5a050415b2c3579b2994fbaa9a97b7f24a30e7e8bba68145196e0424471de31775aeca732dad506b16ad4f61a325136a6664f439e60687e764a009db3218f706785b98b154a625c168fa2ac541b8dce39682172b95e19424b98ba985622b2a23d8db2d46a7230bcb667372a924aa4d1a9e9cc1a8dc1ae11fe88081d74d857bf63e9814a08c9ce059a4b1ccab23a2fba765086b15bcba135d0937a064b90663ec69ab521746600610bec0a6b3c705277f90d588a00e908dd075059de6c52fe05fd8fa8ba1b3c2cf151cc810225db79aae0328b8ea1e92fc67b7bde405a1442128023128a5f7089722b954f15597ebba3aa3b55ae947be1e659be52af04c3205492387b51e72746a904ff13ae41964281ebd81783528269ccd6d2a7845d790fe4cda8ee58d93de3a3ca28bc4e6e18e812c313452a4468049a86ae39d5fe156fa2a43d29161bc911d515f4cb6b625f4679e37d5b55064a16c3e48a7f3ae10a09d9ab7f0ea4dfa2fba9da412af1978720fc47185c7ae16474833c8d278dca65b822c5526bd4da67c0700166cba95e3d23691e6e1e86ea4e560cddd59af18a689c4dc2b6dc0e114f2a84754498a8e299ac17a12fd199d1fd8d5c31b0a40ef4b2fc9627d16ec11241fddb4fee6c652d0119f7d60e852decd3489cdfb75c10a18f562154e542614427ee7cb0e96b99d74eb9585223760bac37de51697d86403ac877f823451d8c60e84188a8fa6804c3a8298f223169c4c61fe443434a76d6a51ccc1d06df3b962fdba9e27d7723a8390f4672d66dc2f756263ab928d9d14f6d550acb286647ed6dfb091c462cf4756194ebac9c94bdf7ae4ac7b5093ab9cb94455dad8ab15b134d31947df478a1aca5e28e98020dfdb6d4ab750911bb2690104b9606e78c8659c2f511bfa3254d32e65aab180ebb74f9848700a8cba3a1a546b77678a17716f232e57b204157761161670bf654f182a14bbbb1c16b14b652127241167e898e097b6270b062f0607a50bb040868768a24312a8da316dbc547237ace3a160b7835c805061f8f2723386929835251945908058905f625cd9a91eea83c0f693523128c68613038e", + "slh-dsa-sha2-128s" : "0x9fc24e59b10d63d59b6c448f79d029745c4f27a31903ad2a6f6fdd09b3abebb1" + }, + "pop" : { + "falcon-512" : "0x39916580739423a110970dbfa8b6f06ebfe827b6503b1629f4008d64024e63c71af7a932f3f29cbf08eae4cca92f36f670de2f3a0a461999f431bd5da16b868d2230d6403158010384f0f98d2b95936e0a132f5b86f5d11e32ecf3cf206ab9d26c1cb7fc93ea3468109a59163b15391795b76ce450813b28d28510767270c1c474632bd6eab70174350d329d0039ef8fa6739b2f665fff021cf669156e3797da72e5913d08c94a48a16d12b5ad7e521b730bef92d7cc277a92b3e4c9f9cfff7cb3896f93dbd495637c2a47835f2afde4222c8dd36d4d6d9cb419b5b5563bcc1a61b640e70bf2c4bed6599c5ec50643fa302c227ae56adc3d4b044926f4bc4b26d58d0d0778e2b8ec5e18b5cb9ecb6d7a26808962082bea23a932afefd0d483a9952f98ea11b629b0ad74c367e09cc016b817b624547ed8fdf360a8a06acf7b5f0d3c92448d9f7e9453a4e29222611490a7188dcd0a8568b67d333228454cd9cb99fdc9d6d945a64d8d96348120295b8f2ea9e95f92d9dddfa2a91b6ea2c44de7b605db8e3e1019dbe5cb464e0b211713cdcc32ef3631caccb48b6b0c968492a5f1288e99ada3a4d8287b5c9f46236b4b3fcfa55135f1ee4b13e3d6765866a316d8cb3ce80d67a518ee974976172f91eff1cf973631ade2a2b81134c39d0f3c72bf4fe546a7bc7d3f1142aaae3f5b4e8f3932d9b516ff1cfd827e7395d576d96cff52f00e76abe5ba3f1f0887fb064ee11096e52d441d1b4dc7a69462acb7296c489f498c456b2336f2f112391bd7e545629697bd096ab373a34147643199222b8f1b05a95bba5877aeed29c43bddecbc814a6f2bb38d1dde30c352e06f19bf52717236488848599eaa1ef17873681e91064430aa2f4a23445fac0a8c8231c8cd7c27f928ca0ebf7ab08feb53", + "slh-dsa-sha2-128s" : "0xe922917f6a5d9d49338e445586f4b3bfcdd92b89a013edba38e598aad56f8d10875fa62dddbce6c66865eb7d14067931d640a3b7c5dc448bf7cb6309943c2f0930c78c03fad2cdfa1c21689c6f621bfb1cba1968dd28e66ceb6cd5cd4f10b8797a53a8ad54e93febced4dc1c48fc4bf768b85a811866f86f004fdc340628ccf44b295fb5310898614b6a0341a2456b94930d086ee75f67bb935e62c739fb87407975f78f372ec11c0bf8bce5144abbb2678711850174134e6e5acc3b87f14022d158a57dcc7916a8e75341488af6b1d9c0af90bf2ea86440fd1b8001d7fdb8ecb8cf392498e41f8be5c7aaa81841aba7422c45fcc0caadd52d36d1d56cd02d027b6ed9d122ab3d53597507079bd9fb476105b12e3406c1873a8bf6a5b9479b5cab9c370b79425b1bc7616e2f1f66a398c2e38f7a9ff1077918fb3f7e65f025d4feb8b13397460a753b1999b3d351c5b77ece3751686c02db0ea4ff23727527cdaf038ff060a09689a13fb7dc902cb82844b11f8092625272d8888090e60cb1040b6cc5b0ceb6854a7fa769a77523e7a718a1a9cf56b94be12fe8fc4d319b335757c6b4807e17e9ffdbb486d9910404e675648d069c55e96894c7b2ab17d91aff88f44ad609142ea3747117596c98831338c616cea7439b1754390604188018818a5fd2cac7522b2df5e42c14e77b7383744234da5fe1a02a60e18a178751eb9c0212cd847317d64dabe4407c4a4d2ccb6965eb33699c37c733d42e66f92b01cf8b0ba2c8d737711439de358756ab4ba374e7359f8ec5838f531c984e2903026acbce89ff2935855ba5e5f3d85c82bfc3975ba3d4a9721ffaab1182035bb7526f062466895c5900f854fb52f5913591828cef55cd07d70c5805327cccb9822860f32a8e8f78806e39d36ea7be59186da6ab5f1739b6716323b9379446ce6e50345cfc9b769162ba96929e2cf0693b77a558e5386945fe5c6410a2594805695a4cb602fb25993a0359753f4ecea67d0a1045a80054664a5f589dd823d11b1e440813b3bf141f119c38f9595c1eec85ce74fd0cd69ab783ff7adf32746d3c4db1153df20ca1ff94acfcf3c8ece27b9a7819f7751028435730771a0d7eee4d5a05627e9577de7a1c0d16400a81c6d93fe48990d8d4eb942233b6e81f2606c228423d52fa012ba6de7c24ef446f32ba1e1bee5ab78e79f40ddc3f8279277735564901682f03ffbae6176aa8c2dc6871cdea56cb875f25c88d955ad2e5f560fa25acb5d2130327aff86a324aaa721874c4276667404738fffa534f26e4e0dd67a9d0e5d447c2868af3f5b93dad621f528509fa6c3d2b9a391275396d3db6487ef219bf42088433b79d34ce793f024380319f1a0017c0cf821d998871fd35d2da47b774f6c6664e54090d74d087fa43ca622417314ac0a7f2d23da0e9be5242191b9096ab0e200bcee276a787ecb770a60f975c2935f24600869822ce96462d97f34c23eaa50b443a504670923b260e1698eb12a76a094b9a8b9bc2fb07c38b03b495a70e4976d05477ee88b58e2d66729a91f0fc003e56236c5285869a6eba073b36efdac3999ca0b91408282f83b179629bebabd5355381c4435bb173598ce3496dd8616eba46505a64d29a166b51b1033cafccc0c6cce63afe4f2dff7a2cc62a909853b14b40c418d3a180a922b02243637700c87bd0268e3ba09730a2e2e20065a7310d8cb86ae6b2174454ee4c01fc86c0441cbc51f1801a021da1f1fa4ba6cc0e8e35a11a0e55a4aec60d5c98330ea1822991083deb2a80efaf382d29cd0b04fc2b3289e024814281aad94ffde1a837da103a0022b23f223e63b852ce8bb89d50271b29b5dd805edf1f4339bed98ad4e4fa42cd726f3f70c7c434035cd9180a02ac954a1173c2694f0cd59942291c34a8071803a69552d71b8191c4299add494d05e838803e5e9de4e5a2d7b0f8b4a221ce677b5b365e57284f4b74c973c72a565422b2233a5b3770cf28645a18ce1bf7cd0e726566c382d801e655829a7c5e2aa149217075014492b80533bad2c8013c382aae26cf4dc39a6eace929903894f85c8e97e4be1df2bbd53fc780c46ed384e01fb40c515e88bcbeb2005df6c152f0d169a90ce3a43c2c3469a85a456991996f6e9ffab555233598f767b38307fab61336a8d57c1f6753749d46e1b3032532a5ef11082cb30e12bb6b292d0f37806e6208d4bbb7c7cba1b3b410ec1d29dd35af7d38ba79059999992088f06b587bd9570ad0a1e2352a6133a50a3a45da27fcf63cb7c6fa98e6d3d3d7987447fc596edd9a157d72d0bb60441935481c54f9c163b985c708ea1dc510f35f9aa8768e73843ba5e8621912f43383365f534f3ceef986e90664123079f7355e5a5193b0ca1bce0443e01adc00f18f8f07a9038cfbc672494715221c0b8c5dadf7a66875ad67300e5fd662ae7b4281f2df29aa0f5b7f9f70fd3180d7875aaf784eb3da36f9fd8abfcbf2f156392cd196ab952669a531bf941b48b7d2850cb8a66addeeb1447d89daac2079094bd4163d9e0d8ed52e9a93e31a7d4f5d2afc4b7d2c0b988d41c3e247237363dc05dd16d49d159005b42ab9def7290a51beb7811e9f4fb9961a7af09fd4f291f797f8eeb05bf76884ca340af07f62f587dfcbadb344c1b350960686aeecf772f59d3f5c9f6c01240667d1fa5c3d84dae1f0dc9562543e4636d81a3f0407bc29127bf4b4527d0f6d7815a941fb53e6527d5940739bd369786e710f6b0f69b05b2e0c49bfa51411319b9f7b172fc11d901e41ec54887a0fe5748b1d2403d23ed5494b96639126c1d3fde11d85c8c717fe8b01319a9668b109e2d34a6ef5911aeb31c88b71c32a6723b0ad2a28b2d8c75087a546550b0971dde64d3cd69434914d5544a8828c6e7775cc802fd71e467e0327d692cba302f681fdf0c7425ee580c413d1a3904974e55e7ddb19de793541a7a4310e50990744b4655e72b4eb5a3a8c5fcc09a545198c6475dd26a43338753855ae4dbd845d3122b16063b9d4f1170b0fb1eddbe15bf6a0f935565ea46bbbd6c00491a9ea8288822dbfe8788aec3eb3e5e42140fe3cdf16328587b736242c2aa85828dc84352bea3c6d985ac48437bd72f12adc7fc08d03371992547b91a3061c9f9bc6c32a8555df262c14dba813ae8e2f9c732bf7360b1e94498f5a1248ca4a0c020e381d8c25f4924f91bf0c649207b1314bc0088d2f15f662a2594962596ea66c036677c451e77f051cf9c5f6b83ada58109f68b415af541dff5d6cba732a938ef4d425243a3571f475af0a456a7ea5afc7c78fa195780b270ee1f4d69fca7c93087e103595af8757afa5dd23ed17bd723c1edf8701a1b4bcb2102ab2aaef2df7cf485370d310651f6e897cd2d687375d29c7f101aed941cbbbc12ac7ad60c3147e41fffafb766e548f0e96bc911f38a6d1864277c170fae28e4b06fee1d1925923432bbe28e41f8cc19271f68196bf4724278f8492a6d5c0826e03d8dc1232c0efe5c7598b71be7d6c468ccb1077d7c8bd4630bfa4a108d58d5498e40584f6ab690e27ec8152c1c39bb70ccf7c5f2c33e48af63d536d9e7ba90db931783a9cccc05c22dbfd90f84f75d139aa22b1246a15534f1521ecb9031ff0686c39b728fcf6e0323cda56b641561a88c6f78cb1d880d102d26b20213d03af41c91ba6924a0ae17a92fdb375492fa0b514ce2fe88fb95d72e5b1b11bff99d1b0ea0832a0d3472b5880b3ddef0bc0891833adec1f7797f86fc549b22df2a0a59390a900e588cdd2405e5afea987db98554585c85fa2428cababff59c17713cfb100084192cee9f4ee3855163ce76fc59ebd4b23766b0ca7c2d59754ca42ebe3c997cc4cdcfc2ef455d3afd488474b4da2a6ba278c196616f995c4398710c291efccbe36bb724bff2d097095f440f79917229a6fce31dbd89e58696c4571dce1e535adf5e1b0942fb5d59a13f7fb4eb17647e83e587acc50d920db2951bb48e1495256e10a62a8d43d3cb7719c54c24f546ce4937e2ba7b1911f5684068c365398e41f7153cb1e2b9ac014bb7395a36efff14ee94373dd128d725dd6ae6af408a5da12ee540fd8d2264b24cc7fffdb2859ce5227ec3c3d5e0bae49b5f495a39e5d6b3baf7c967b21a55e3528fb0bda0e9f2a3cdc37904005a76b07d044ce439a345f5ab69a3db8c711d478288df2d4776d53ccfbf49a06d0d0332814d34053c98ee3464bbf46309b763114ddf1073100c34466cbf3414b4bc7b8ee73c44d76c46435c0354a71fba7d8ba8b0248f867cee79a4beacb5dee9c45577e5b423582fd261b6164de192e98aaabb47179a6fff8827221a70c77df46d01357bf27392d4dfe3b8e24abe9854d3109b533edab16f495eaae07562dfb4428cefbffd748f144329f7653931a9f65a3b024bf19d2fe04764d53730df662469a19c4bd966dc925b1ac52d9094ff7dd7268697468135000a8766d8292f9079f9bb5601193df637f8192812a9a97f604015ec15c89fee9f700509cd745da588a86c7c756bc84745c593bcb7c7d9d34bea98d505a57b31a90833e3d1e154c3b686d5d8a20375111bfc6964bdf2f815e160754b56c59fec8fa533cca9e21d4f688ed94cdab427f9e179fd308856ab7f7206d13d65cace5f8d65c8b95270559a7d33c76c81794ec38d75e2b3f9ebd1351e07da0540e3981d447558d6c21ce6b0c32bf61a3f554cae9edac7a92e2e25965037830c57f30a021953d91c8349b94181ca8c3bec9f33327caa08e437e542d4cd9742ea16801aae94e19442030e8c95bd4f23d88ae9a1f1cfe4a80b046f2e54885c54572a215f17bf8359db4edc830c48941153bdff79a6777a67a9ca0ee3c5d2e4497871f4797974c1d932fa3417b909ca5b29dfb315bf1590829f1cfad84b9e068213b8a6e9171c1c41757e262d371b7073d1b90fde5bdbafab76426f486581bf52fb2a53df2fdd0a6d5065afa6148606a3dd311b155dc0dcaf9f741a50d105053225f1c223ad0fd8af47d27db808cd32f733fcc8e2655523fd36a906c6e6a1d6eba56311424458b446bc1b77b866af76be210b9a417e87903a1ba2e431c176a82cc5a71a4ddf29350efa6dd8d1707d3884b31b9b52bc876b6599f40b077c43d815d2e02ba043505abe7ecd7dcf73818ceb77a09ea4e582616f4af11133a80429edc9a434f3cd2b41192edad842b433ee7936fa5c7680023f0727f4337c53912fdf6fc7e6d5c0dda2e0a8722d5b0565ddd8f9349815bf38e8615b5c7ae35622d7feb7907e6423c8aa7bdf57355c0353da377977e180b88ecb1faaeee2d7142f342263e636eb99c78af9cd0ef0314d9b57fe1176bf8b13c221b5a05c1f876194ac26c35b0ddd91a2130276ac402314367d99c2e300a33073c06dc18cdf5361908d58a9553268c6d0ba19ef6f6d0bfbf2b1e2f5d284c74695189d8a3236e927d9598a4be253d0492f98015851be3693b0c1b3c4a73a95ea57c20699240887d501db5d3525e787dd1a583c2a831a9b59886c873b8ea5dcec376ff37fdabfa21b74b3968008dfd9784fec35696779217b4f5507ea15852f751af31243dbf857d66cdbfb2e290b52f67312fe5d93bdba25b1b7dfb0f3488974a7ff0e437cea09d0baaba7bbd2506fcd182cf0dabe7c262a823fa1dcfe00bfaf7cb5f54283763a45134dfabe9927fecacd648ef767fdce12d570693ca6aa10c74c41761f64f31139f3aabbabefe6c7bd79a6a3334cb465f4203604c0288c49db25bd25c6c4c113e09fbd65c0f271f4a373294831c33f7b2ef392e4a4b91a3973d38473b9f3a485cdb04b7f75a833e2c0461c6f27e6d8f518d3a4366ea3bf37f43e4408f00c4c72ca8d0a9fdbf1ac7fc9fed47e8a3f3f33ebcbac25fc2e70bd39a85519181383597ccc3503e597a734c98e98a15be8bf4b37330f585ea138e5a2e4104ec1cb6f5a23df7ad83751c32432033f072e8ee3cdeaae4e7de98f9e58633bcdceab69ff60004cfdf51b54e9a33232e1df86ca9708811a69b8759e5b2daf57eaa8f1f74ae6aedfd9106c105a6cd715b66f561c0a0500a501a9f3d50a0b5540ec96d24fa5f6157f5465d413b204a224e2b752a49322a90aa340e4422e60a449cab5b6106135ad34ae309ada87ce9a99cab959f6c2502b2da99d8ef296884f4255fa88f38908e4294f799afbf3d664900c5230ebbec54328167495caa99942c13e834da5c488f2dcbbe4d2448eea2657a2f82a91a53d09b2b5445de8c8c8ede11f028e067620f2ad83be80d19f3b9a9f3b81bc8a405c233b2b17ce8ee5e0ef05847aeb36f219817d735faf6092d13569cbf069696bcbc50cb50cbef921f8fe76775f1034e73bf04431b696eae4943efd78ef6c19436c76ab389825e57b23f4529fb848233833afa418d85a92fc865e634357071bc415da56078f44bfe306d3b00574c04b2e94d6b801d5e3bc03686d1bb27dc2753112b2c22a43610a4dbe1e78c9f4f6f2b4ca3ccbddc2ce0e78fa34cac5935bf0867bf14f40648c805876e61fa841292ef2d2d53ecc00319a6df0bb6347dc865e39d9da574ad93f9cc910a9eb920986d5e6b331cb29b45ff32ddbc9e09b71eef73c8673307d6684a34ce85ff3a4208741b205d31011555b17b7c5effc210ad9309468a007d76983011b88c818eaeb3d3815a02d76f735433192f1bc99a6c7dbb57e107caf9e986248ebcb2e9c28279ea1e19a8f8027bd6f8728c9aed0b974322254e686526cd9ee0c6720de4eac01cd83e5705570c249856ebd7ec76cddd98cc096fb93db8ed86a5aa0736c36d978c09b915f7b9290cc255ea1a4b355888fb96e506cba34683c6a890c77d92fb01bba6b69f91efb3e4313111dafc3a189db063195bb650e0cf353dd975fb5c286f86b8f7d982ca5f74a78fa0fa3d693980be09298bd41c4ef38874ddb95e928494b9e345525b00e4760a1d6a4d54675e39b1d955e20a58f3ee65ffd1e249eb3558ba348a2be114f396c422c57314ce6df0ae9406dc6bd0bf5f1db49f012a03834b3314dec7839860867033643aa7dab3fe17ee025812ad2c05484abd0bdeb67043be2a57533d294aa03f24ec8afae512537343eaa16b07eabbbd2cf6755261ee473dc457ec36775102e1c8150248e4e51dcaa6abb7753920bd5247ccf6dcfed9dde98c9f37938d1c824fce116f39f0d67ab9c1fbc79e034e8bee6f79715433d485edc70e5c098ef7bcf2972171da06cd2f19cde8f824e0f2bbcd816f7eb87b7bdad659a900f36df708d3446d8e412b92142d8414f3a0508d106473e809a0831953e2064c655519a8f22fd0c517df7ac44d72a0e7b6fb697b188133fdea4d16e51c6da3109f7cec1a43fdf47957a35f4651454ae04da9e9d5a1668f46cbc58422c38fdf204f4e9c0df3b1f0ed2e14d0e008bed642353ede25a5c25684e7d593eee9d14b3a5a81429b30895ff5d1f7f4a124d2fc9bc1eda61e8e4bd73b57ef1a168cdfb1ce1d70955567a3d949bab203c379e3676932f96c04ddd0271a6afa0bd55988134de26bf5d6c725c09e04aaa03d393a82805a366e0d98e4b65bf45e05ce45f4dbdc1f8ec052102df08f5852630b706dcde9a3efebca3852e85cbd7fb6e6e00b4bb397004db1602b7f8c0dd365eb2d946d00eacf0ecedae83d26555a1bef8be88eee9de9f9811074151f0cc747f36d45ea7290d53c18fa8ce73f77f54226e5b1719a9bc911c9d7565693836edeaa6cdb7b2e2f9cc8c86b7c207cc14c261793df6c4c623355db04d5eac01c011677d26b764e9048bff8d76e2b6bdf792c57e044eea4307f5a3a62900df3f57ae88577644e2121134d61bbf3100fedf83371438b7854d06340ffc00383a0a4789a6a85062aaad3761dbb3c77e61c5926b0488f711a77735dce429b6e028373dd341fa2db5394867242331168665b4d7fc4ef96b12a571b8b25d44fb0de64b9820734e5647821a038428d67dbe95885526c3f2b8064e8baa5144b7fabbbf5a133c4b8d7a2f950a4fb69f9cdb37ac28cf0421894f0b771f80db988e25fb4ba9991664c93116bab82ba808693d583a2b697fd2b5eb673f954255da410df58231eb7c89b8257c5a68ca0306d088638b2faecbe3cacbbf77725e1685aa81d965f1ef663f1e474eceecbd52943c2e4e0456b52aa0c7758cd2fe67f3132176113a16dc9bd11d6dc03ee643fe8bd6677f2ec0ea754ab499e00ced482d78147ecb2f91d3154e03b57dfad4e4cdab98e134a609a7512a5a0e7c3c4adc9db444238e0fc3c3586b389d8d090ca46222a9533672eabe4fabcf61beaeb59c507f2941ced1ee4cd2afefee5aebeb354ec33cb85c9695426126f016929b471b9cc07dd6ff787fc2d682cc1e80657bd656acfb865dc91d6b032785cdd7a1d66d4d5463e142b6f028e8fb5bd6c621109e7022818b39197f85fd6ad4785efe48c4509e9a537f10d4fc9d23f275fb9e684c2f827868c9ff3baca8faa59903c829bbe1992541d15d538e64329e886ade0c5517cbab3b15fdb4e82506e27f449e268b488de32fd036fcf80f6c97505a5f33e4d3a004772a8970a3fe54049513cbc57131ffca1c82918a81bcb879b8f48d4300b11cd90069912ec9e59d10b6bebbc24ba1061c62a8762156df586a4aa758f13cab5ab59a3b1ef7d038c68920eb0a3c8a673c27b6fb80e5e8741b3b7fedff0c068a83e72373da07c8d73abbd9fa0084bdc92866917b546dbf7a6304f04b18d9bcebe2b4bcf5ac7ec526abb4a52a19094c8c231668d063c5558bdef0436ac418f42c5f41f2df996d19d48f8bec412de05a6a3b079e5b6ac1be0773814acc3d20166874ef64a6cf27f7fc72e0232031ddef1a6a4ac9897a86605550dd635c995626fe985aaabdd937bafe3e2a4082f0fc875ba7ea963a8e5987ad400423967bba1c0e369263ddf16fefc88901be069fc617ba6dca90608cd4a9d86476f8cbb41fc8197a9ee6fb331aae55959d107bef29b4f6f064651964999fb8dc904b8193dc7582fd47b0b0fb912f1c0c3838dd489c374ba5ea614cc80d4d196291c4a54989ae628dff5beb61612353ea0cdfd75962ab057bac887ce7f5df01861c769b88c7e1682f0ec6f7cea63598c3e6f41093d845db95377654fc56702a4110810917521ffada7432b7b16a64699e6a955a4273fe9220976f8b1ae9ea79678a3a74344a44c32454e7bbbae9a1fc440811693f66d63663c84bf847e0a2e8299fb0a6413194231a666a2efe5ae1c41db1cf12dbd8d58408cbeb1a9507b5b09ebf416f712b0040f8300dc5188e673c5ab1f13156718031f25cd5156beb8e77e95b48f42d1361da25a94cf1bb4a2a5c41f8da5c3f6a30c00e7d0323f7bc14583fe20815f468928fb69a480673b6c775b2d1af6a176abe5f6c679aae41ee1d5c89439a798823b65ef718515ceed5a7ec87eff54479a9afb48d0fcdb9a240e946719598512220d2afb20840e3680689cbbbf096e7d7505d1d854faacf024935a672278c5dfad333373015b11571b2a7329a0d2fa792d838ba3fcbe5303f84cc6d0454b89cf740bb0f83b79c98b38fa17dca42c5d57d8e86cbe16d46c4c0caad1f55ac4a399d9755067b642299b6b023e47ef74343b9d856545a51907f86034397dc1cdeb0856ea9fa753ba82c5f7e44e4423684f1b03ec59e5cdd96c03dbb1421e89bb0d1dd570309d92519e2b90fc83ece91ec6b13134cad1272f0fb1092a21e0066cc0a783e566167b52856fa6ef12aca963dde340e94f04980279f67a1eaec339d0656c09a3fe91a2b1885f1c09e831471ebe78f2af2efa1fe5821034ae172082de7cf5fd8599f7c575311d35248f2b0e6342cf7ac4bbcd19942a74ae9a9ea3ef5641f70ad1746406ad9bc24a1a3ed17dfda75ad54fb920ee9c92b792b76d42cec2c22fd1bb0f7acfa398d82b5f016f2943dfb44ca1d96b2a54ceb7595837379d62feac76dfa610a82ec947047176aa74fa8d58aef3d34e79768ba7b213a8c44c6e9739f05eb197148b2d2f3c5a1dbf0d4ddc4ae2bf0eeb93d2d4d0990c2c21ae34300a3139ccb5cf216b4031f5be4fc61b137a952fd516e45236dc791b692910a09a600fce9c14ce9e429ef1ae3ae737ebe9921d10522a4507d0794fde66f639189cca95923c51f97cfde4d57c718b6e0ee709227fef69a95a229ab54a4ba9d858115ac04dd0a594ee8fba7d29a23d8df53c131dbd6f7f47c6ec482cb2da38e98a8bb51a2878950877408f0892199ae4e94c4c8eeb614eeac1435b2947ba67c30ff52ff4c958eefd318814a17f53c8ae691d3fd91ea8196bd82d4af9fa216e32636d6535d5c68f579d3066654f2cc067873fea5d56f9676800892f210363fc790086a778fb721854a0abf9a2403ac6de964c2eee9011a44cc0d66ced8bb0634cb986a12b0d5d875db0f26f008555f7ff57f42a441250dc5d13dc5342ef36c288127d46b4a2cd85e5189877003589579aa787c46843e47666be2ca6e8aa84e065ec77f0f77e5cee75785e2358a5e1fd7d540ba1d36052964d7a5de9d1598cb71b238c6a6f2a4b6c72d8c5b0a4c2f17342747dcfb2bf7bc52256fa21741e1f8e7c2b749005ef240296ff9935f75e943c2e725e9f54636c3466450a49eadcc6f330558f2146426fe6786f4ac6fed1943d616474f7665e8ecc2612479eac464c8313066de5fb652beb42ee17c0d9eac4496b5a732c3df31d90e24ffa8eacb12b72dc1d393d0880959e0ce9db8141ae1490bb12af9bb2d12da5809a348714024d7beb73dbdeb4d7adcb68c59ada159fd1855ddfaa33e0b985d7bc3f509e64ee52b5057656295dc669939c45d89a07f03c0062e20e309a0388725c993f74cfbb8db38b85f13bae8b27a4da755398f228d79eba2ad5101d80c75eebee608dae268dac0c3a1c63e311fd82cddb78f06fd576314ffbee8ed598818894fffa54b56e7a7be5c20ac553d106609f4bc757acfe7b116f695fa4ac3ac93bd0de4bf87aa6aca5535e467298aa934c11143bbf39c464db663b97ad416b6501daa12fa8d9aaa31334851f36f654356467cf4f42fcf0c4cb239c2acf7c7938c0760a6a8f953e75471881c59293285241f7b73e4b3dd5" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n13-rotation-unchanged.json b/consensus/common/src/test/resources/aere/registry-v3/n13-rotation-unchanged.json new file mode 100644 index 0000000000..76a1d938b9 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n13-rotation-unchanged.json @@ -0,0 +1,55 @@ +{ + "description" : "index 0 did not change its keys and carries a rotation", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : false, + "previous" : "ok-epoch-a", + "expect" : "AERE-PQC-REG3-13", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 2000, + "count" : 3, + "0" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x398b261d0e1e172f9837179889a04cb790cd1c950e473360af30a5ffc3104a52fc9954ab058bfeaa8c2461e0173b276340fc57304451188b40f8514af3c4efc0209f2f4eb9aa21374ac202ea39348edb08472f9b4ac6138118ec48d524c7db633331ed4aa0433ef629d183b523fc9d9ea70355ca1bdc41daebfac9cf17d704271d9f36b7a3171faf1c7be709222b643d9b71bcb068ca322959ec8203468ad0ec96db7eea7d5657e59402f31486e27b50667e35ae6acd5a4ad129ee2d164e7b2d9c140efbaa843a5869075588891359098b89b93e5e4e6f006399dd5d2587efc5aebf7445c1e61fc1b530932c6a70ab2586e8de73164a031d345731ea341182e1d9729a0309ba620ccc45ecc27090342645c04066d497e71b7b847f30cb6748b47b75bee56ed101202d8ad0dbca51748f4734b190dd191852127d79b4682cd7081ab2a1100854158f7f6439b6b3058c80c232b2f9c2f8df205f4c93409fb51a4373012c5609c6c567d665d43d0d32253f5238f0d4be0f00e8eb9779371cddf0f1372a93f2ec72e56cc2a2d6eca7cd7d14a630ecda530120317b0a0252f921d8f2a279b4213f3719386681206c88991130d48cd9f9b87d965f9abb0aaf0c59406ff5592675374a38fa87f7384c0b4b5f0ef25cda1cf2df5cf2f51bf82427114ed4aeba3f63830bf6cdee15b91e95b6c86d1ff88683755771b8e8f65e28aafe4c21096e778d196acb2be8cdc1eb81bbb9135ee56bb61553f36cda6f9857b91b39b669d282ccc5a7693cb39952f4c1657f7d02ef18c7288dd6ebe10edf147ccd236bda94abe49724105dd9e3a86f57a78a7c5616464906cbeab453e49d5f75b81f5f778d85843c71e97c520ed6b9d1ccd5a9387e5865695e9261e0d8c748ee9e8155bdb696575f4be0c7d668fb78", + "slh-dsa-sha2-128s" : "0xee44a0de25b75a42e32080200c89b82e2f89551d44da272720c816fb144aa42ca29cc7a0045d97c82676d36286a9f1bf4d5b9e44b6855d90f25e03e3370cccc7665f3c7fdeb6f9565b76745a7652f70b7cb18989582559b81160fa32c7db7915f14c657ca37224955d174cc69de41774e4ac576f362306f3383733b322f8b333d9d0dee44d80e36a86491bfb7441a3c66809bc600399ac9788b4d7796604d4ec423c40dc1a6fb2b02db9a0d35072cb5aa617ef769006c504052596186ac0d504561c815617067f669b29757aed1de7a03c9c0410fb8b040dac268aa95232533eef1f69cc0100b33c9726d55af73b5d608b501b0470c4e9340b68251fc645bca481dc2d375de626580faa7b7faba8f8a596f795481cac23240854839a1100cd92a6410291ccd40cfdf1f70981c568b32904a0ad649de41b32299dd5a8bf50c35e5dbbd474c258946881b49e335e01c72ab6654741e657bcc5109fbda7297999cb4b272302739ee07cff0d1c9ef454a5dc5403cb7c170337336f94d56f6ccf563af44bcc8382de301720076a7ac1edc1ad95dba129760d7e9cd4df609bb8f7b389e7ab1e85e1b54fa5e3f2163afd7e24c5e5eb61a9e8e79dccefaed30f5fae83bdbd8a1673b36ce894891720b476652a26401e7890af9199bfc74fd0aeb6622fed8e488b8151a6c51a11bd808702f945ae9f9a016bdd94f45279d4765bb376df7fed5506b52b74c867c087c3386f74b130cacd7bcb4e832b697b50c796fa8fe4881c3f994820799417885af464f4f1a85989e5e3b54c3bd878a7ab054bd08c1ad850c96f3c0105380323908fdc94771609b10e489a8445f751f75a9b617cb1aec4460be2b53920e52f7d85096065fecbc6f19daae458f657dfc894758f3d9a6519fb4276e16b38aaeebcefba89a7686546901c57a29b95266d7ab87155a854ba9b269ac94a279e9c4bb38e6a4928cf6fdc3984b4363cc44e0e3cd7a7edf08388b9ecd5a04c55ac26b98f98cda79cd3f402caba28360899cca559be68d7a825022f9010e65f9a6564fcd0ec1cc4c0f4029086c90288fa83656cf2ca2cf30d5c423342cd15b9197905e9ab30a9d147aa2599abae71070e40058f0787ae39f39d6e5c1a8184b2cb3b1e6e45c40c61d46af8490cc9af6422a439f16adfe93a3cbe88f63df5eb8fcde9ea111ca96ea7be416d030500f2c7cf967dcaa4d3ed76a613b2bcc034ed3aa84f35556e7a65a8798825b576236798318034204f041dc59a9f9c407d34c2ca2d77470700ed6442cea5282ee99ceb71c6bf074423dc0785a39b89b062f89231af868812777536d0b144212d5b4b6fc7b87ea04fcd6f94438704f985b746a13dd519c57260f87150db4dc483b664944b9e805197df797be099940fed468b1e214ae99a68e99c6b5cf3d30fdbb1b3cc009ea58958eb4c4a7b7bad6b34fcea2a87eeafda01dca5223458affe54422ca70ef541ca8437171943d5bcdb70eca8de02b93098653160cae52f4d72fe5a6a7ad51c6465388f0362e6e72a3c96b9cad2248e8d9a2cdf89747444c7e51e2f89eb19daac76da37180e4133e0dcab15fc72c7bb90c1024d03eb2e630fc6d2537a101961b388e32ca5019a626b5a23914b06e7822905a5e3c21369b69fa63fc9f43cf7ecb6749c668c7a940b82f88c71802ce6e3c68b5c10c9249ba356bb3bb0539b581a8bd6ca141e5dc2b5932251746c1fc1c3da475581f3e3717f5d905f5ff3c83f1523f57cb750dabae0404beb55aba1105b995268906131835718cd43240bae9aecb30c794a8b186436cf752179b1de65b2a87917147612e3c95288a3e816238b3fccccae1da8041b4f784ddcb3d37ef3f019ef997bd8a4e172d397106a5adebb166c5f565016bb1c5f7f1a0cbb5bb22cd642fa4390ea640c04a968fb5a49ef929df1cafbb69c124ea58b31bf066be388d7b6d4c52409581da785d6447fd72b54949e4c1ad08fa64b520f2b8df83bbfa417952b87fbf802652a65df0d388ea32dfcab3227f645b6b28b66087819b4339ad171a48760e2bf90f1a01184389e23e7df583eab7c2dce385bb7a4d60ae5573966f916bb84aaec6cafe710f839b103532f9a7a556bfa116a05480386ddefa2858ac7ebdb58664a0e633a53e1685d71d5aad11f5eeb3d9ae30ab120fb88f96f383bb86917b72e02768f071ffca7e3fc2ac79846a36f6bd72c58aff03f9e87fdbeb65c8ce5976255227778cfa40ce0eee943b66a382e172abc814ebbcb0bf49efa75f87ef8621d227224b7be51b73a928c23a8937570e1a046bf1fd4c6993a9c14e552915953776265f78882ac8302f70900f9d612d2e7b7dad88cdedcdce7e615baf89ef249d6d535f92a2ec90a7435785f4d0c7a75812b875ec22e638f4af1705cc362ecf52c67900c888d068dc6f72a161013c30dcaa49cbf25c56e8937aa4897451061dcc7579ca99a25348e76ff121983444553419e820a3d1a41744f4227dca054c454a6497e0762155e2a06d17acb44ea55f54e4d6d2e1631a63d9494cf69c38ba66deb9d95c0ee01950dff8e6a5d1c46fececc7027997b8ec626f8405ae99ac20600898fd7fc422e6cd3f774b4009f371ce38e1f089ec93ec0a48d6bfb90ee79c3bcff321eeea33a44022ab910458984b75c5089cec0d8beec20c4c39311b991154fac7c0461b7d667488dc9f0f5497cf66166782e2dfb69a71031fd0251918e4c1243aa4dfb9fc2b6123b4f02f436c0f8737a6663af2b99fbd22c6af539ca9389bfb4e55da2c9ad42521ca6978f71991f53fc8cb227e228a62f3e22d7c3eca8ecee260e55084ad3ace0d6379d5eaf82bf6d3b69c4e74fade4210114d95b41a4c004858119a9a9b178d5a0cc97386bfed0dc6edbac5281c2842951a0c4d7da10207523973c676b8e6887a087c1c2f156970cc6db9cbd3c8ec1c9c1bec41fcbfdb4e8929b67e6c1939bb990ee2cfec3197b4ce824d25a7b4c765945e85ff2b096037adc4dd763b87850fb39aa98f1d395c9353ae3b5b53842a7056c011fb690e856401fc6f67b1c0a5414fc229284e74c230ced260d4fafc5fad153a5154d35c3bf9c559239ce18ca9810a1254a0a6c2960a53d838431447739261dc3b055a0b115914af263ac9fce2b512884620451156c40cd84b7bb2d449aad243a8aea3859412476ddbb5eb73028bfc6b2a942839e363262f94795aa45f6579680997d6acd7a8c8daeace08362fcd420f2f8a96801c002361b942493cc5ae79884eddee989af9832d55319ec033956660c5f11b9fbff9f1410a18c0e9a38e9261bdd13bc392a18a3df97aa33e7f59c125f88d78c86058f3c7e9a9d3b37bdb1e05d90375de0090b418de3ca761560f079a6ddf328e72cd89dcb63c9dd0357ec736782696241e2095aa6107a8bf7bbcde26d5d14533f28ddad7fe1b3f0c181c0e6d55204f6ce79cfc557cfb2d54cd2f33adf17a837243b51705bca7ad8cc452e5340732e678274f9eb532bfea122cfd2959b2499ad25a18db7dc387e976f8b5510821cfff778be799b80505cf38ab1ba6b01561ff81ef17f7735a1ed6a319a7f571daf92afb7490fc17064ccb6a8453ae6e66a2afdcded0c7359461278c49d342df84c43a0607b7327853db675e4c9ca95c52ae013461ca0763d157ac992e219a6683a547c32374631b53138676f50ae0d30588fb25de808e1d9efd5fb9485192428477c2f64fcaa862761c7f5505cc650910224400e2a2db13fbea0b0cc358d163c6d4a8e6296e4c9554078a55f23e60de9bea12ac199cd19b97babe61b2a3b853860b4441ae8dcd85d59695393816c64bfc074e945452080e15bcf6776cbb6b251c13be63932702468679b2bd7e1693a1d910e1b62be91e177a8dec8e4d7642e88e513e3a871ca502854ab0fb7fb8a099d0e0dfd73064012051af337abc78bbbc6fa2dc7169ebfde2d40167048d78f12cb5e7c130a2137a0b26922553f43aefdcdcec55bdbee0fa752a4787ea9d1c75d1bcd9d28135515a0ecfa9735901a0eb89f915d036e21263f90606f65e62f6e5c8237db14d570612611b3ceb4d026ac13f473e5706cd7975c95d49734253757f9c1e6aca9261cd2b95722098c5f7c74daa6c926ea4867ab54e4605a15bbf7ab94348d9b45c31a1fbdc7f420cccce25abb4249cf413008a01e53296318414dfac822cdc0bfc4298273f65cda3fcd61236e255dedd879fbf20b8c4843e27ef413c7a1ce2c714c88358091abccd7fed337a7d4f725ffbf7856ea4694113d605b1ff7b2bdbaf60f7abf7d43eebb1e82f4b6b7f3f8fc23ae27b09f78a7a3b324c7790431c800293e08fed2fa49aafe8f5670d4e6bff4cf6e7924993d6e18ad87699da99a2f2ca9f3c7b44b3b61dcfa5f041f7a6df94131b8e5c04069678541c87872daeb2943902afa6ff4798e45a75659568a02471afde44f8e116fff4eb7c16053a7aa370bcddbf5229c231ef8f2e642c1a88f90e0a309d24a3ab28d3adc176665e522cf5bda0285baca5bf2a0fb4c0fefff3b8ea1522c9ef29342ea57eb4f16d69bcf948d66247ed2f2cc4e567476f736d170c98398ac7c0d158f294ff33ec8d2794ec1929c57ee6d7f46da8e40337a2b380717227ed82eb13a14d129069e98cdd65bc34472fe1a52dfa7e466e559705c53d73d97239e38bbd600aaab107ced36fe7d8ef2387a05d8548a48a684fdf02b984022aeb28e8c5eccd1941ae19d2cb12a31de296b4fd1409266c43118848962e9e59a41054a52bf2f20b09eb4e77cca72df70cdc4d712d5c0313ddf9ec8df2d901145341b3ccfcbed77288ce3e4afc677ad8681c746eca02e7f7538d0108b68a922a21d6a8c2cfd67d8849ff5779de1c00588133309d4158d1738e508a1a2ba3351a46fd3f86339d66b1244bc2788de3fc60144d03c08b0ef08914088f9ce947d2b230ffb9ce176a4bd45b0c8b100e17ec7829eb1ce10b1a514b5e33d4f40792f0a300a4c3ba7a912db8e19cb72ee02ae8ddafc2caf116df905640d0cb65cd0c61d38e0a9741d3dc2b7677e8ec051d329183c954ff9c0c2d8771182d5550a742ba8147ec6571bde519c2f57f10aeb1c8900f8324f017f3b1dad92fb0d19a76de75c0c7cb67d079c8bc0bd7355fa25752d5d00ab00f9ab332aa23aaf8b5cfa4d2723b2020c9e494208c61a76e18c9a583172d6ddf544a327c931e8b908830e46f184998fc0d34ebe767e4f5d06f99804759c7e2b76a8baa302d6950f90194b0cc694a0d351374cd028ba6b0414377a850ca2f209d13cb7daf43d14f606a0e549ce14189023fc7b45e2b095a3f45930a72296eced3e2da24bf237959c0b966bb88a3a9818932b1cd41e8ee002df515ec94901902b2e67545dae53e08810b99dade7b579b847a6570211687a62a56dcb324bb442c4fc22f9d85e54b3f939937daba84c86fcc36c83ca87de70fc7f4afed335e8f9aae027e854e3a022a053c3b22be3617240f1355be9af3f4c8e9481005cbef07bf1380ddc012412c31b221275b56c3ad52cb2f7a9b340526e6128416025d4fe497f2d502070653d36f924420c0844b5560a8728e4a3be8b1ce67a1541c00e97aef9c808bd77af5aa280690d98f5ec218bf5c9d135d85069405b82c221d834b8285402f481b7d51ecdca91c4c2b27c12f486d382e0c14ec23c2bdf33ea8ff693af551fbe55e46bd84f335a68646bd092f914da4954c57201d7e812f4974a553f043ace9c6ce2c609cb4d90dc1ceacd5d610cf4723024538cfedc1bb22c9663ac541f89f97b37d2e1b4d8073ada1596e684b94e74c2c2f0087251f1eaa6789ece436b2940d3285f297cb7482aa9b8c47118a0b1d2031fc311689828b64855bc17306529e4b0a3326438b42698dd4e2e0c2f624aa510a2363583cbac47fbf9974925d81af9534d0ccb41e2b445eb19569d703cd8ace661139fa344a5e097290c233114370831d4710676f7e93889ed533454928333655b06ac5ae67f301f2b7944d769892ed76374a1f3861c8624b2a302df1a2f1a60d41e61379ab3681d8eee152c251e1b7b0806aab2f287338b752918cb989d864e9acaa92944f5194f8eba19a18a025157d2c3988163baf8b7ca9060edc2227ade8e05348d86d18f9eb633e4f06ee346e4c2912ed1c763a75fd1baf32cffd0c856ff9b44f1470136c0a8ae0b88175a7e7f993d8469cc8ac3f643aa7db36992cc97011be0a42ff1df8aa7cd82e260e05fdd67304644335b386c7825858a438c51b63f877af005f7d95202b54a2130a1f652ff7addfb798daf4415572376fdd910cd099066e8d2fe251a907e41bb950e960fe5116f4a4fc6037273250569b41ac07002ecf3b4b63f8b853f9693badd2e8bff695266095f4b2f38ee4d60428a45e7586d9d47c01c12c5e18d1ca9afedca6f03384567ddc730fb8fed27516eb7c54135cc7447af831f0c37727997ab2b01fdbc5e12c112aa987e95cf5aaaf44be14bf4882f49cd84894d555655d17aff21f077b2cca1ff0922afbcbf4781145cfb87df585e6af1955db04843a763fa735d33b3df836585c5894e5125d500673d5e4d0bdf6708d8d28c80298a9bb47c7430ae8ec52285a4c14371b3f8ac1e21306b34bcb0c1e3aa7a8dc8955cd9cac32a0f69d0fa0aab30abcbeb2c21d655ccdbae1c2040faedfc7a5ef29cd5d9227908335ebd257ae8c410b1916b9a5bf0e72902c2bcf701a18c1fe13695a8435b0d0129f3db883f428acea8b7f274a629865f1849d4c263a1536421875ffacf058f0128f0d012fd60f2b3fa6b71fbb96f3e1e33faa09b1d20b0919018e91a6bd9595e9c16d4fa6665c4d0adbd77a59cbbbd587029a4f306c887277a7a27748a6f289edfa16c7e4042fc81355b85a994cc6784f5fde054f188b9775836594abd82c1309a66db4388ad49f60794d8c8e1fe279f8965312a1e23c2956427bd6e289e55ec728f66c36526d40aed7c44e5352fa3e2b5d27bdc1f7b3bfc4f095d002bc8e7b2df2032daea9bd3e0bed2733e7ba13c6d8b45c492becabbef0e8c62c257480faaf6aefede888ce1546046f04ce7e06936cbda44c5f33c71c07b53fb3ac7a72c3770838471aace69f7ea454d2980fb5462173aaade7964b888606741270f92bc18950284d013b692bad63ec3726d8f6596788a032eee6eba08eecc836202b06ca43fcdb062e65f8ec91f7400963bbb8094de4b4e1042e040e81130a3ff037e1b7e947b0c3e8b7ab5b539f8570ec2cf08971a1bf0d4f3e8311b5c625b7d032fe2e0603ac1a4824432b4c7bb5afa190afa30c36f168d196fc89015aff8229fd4b299206e8183897229e95ba06324edc1a9ac81aad786d5c9d38c87c48fc6159934a8532489f06e1aa314a057d91c0f7218861ec241dfccce63d60bfa624652795229fff21cf5ca61ce05ea0ad6a1763173675377d1f2427564e397683428fbcc4b97495735d075f84b289b73389a1fece959f9d9b4932f4d33a0907202c650ccf65327d24cc24922d0d675fb59621e2bcd2eb31ed1ec5fddc96593d4f936ad42731a50f164e404b93920283bf9fa43f8be5878d18e07cb8813914508532625f71d02a615bf47018e3b3efed72d17e0c974d33df18ae70e3a4a743ac3c955d1c366bc94d789c90a8b3771969210c7f0c4b9b12f9fedca5d2b71b6c04e7024a0a5e3d0d3c7d30cf568a659cc17309b223399b39ee5efd0cdec3a34b0735df8d7e0bafa40a8aefd742b3e0157fc89d113af5fcb0cd136a9cfb82acce406054aeda30b83ade59cb6771c366c86b5d9b016a74f220a967b83254db3e24d3e1f5d5e23b21354420dfca6bd8454575c6b0e528744f44ff8e1b9679b084d083b9ebeff09df273a476332dfa172cb98c79ecbf3ae4a722c25be71a3b206d848f56b02fd04bd32ff959e6931bf6cba042f9fde027d832632cc40d4320166f9644296d2dcc845491ed780cb609c0432408d08dddc70fa2d52884df47a55a8a3e706a24ac6ceb375a894636e1ac121040561ef486ffeebed3c5336d123dc05a5cf221b9e895f6d3ffb24b3674039d50fec114d3648d6dcf4fbc0b503658672d675c4ac64dac4db4615d011e82bcabd9573fe9e2af224103f99c4e6e5acd2196f1eb2260fb4aa61c298f99c20c4ac1c9e70a9a9e28db441c3adf2f2b3a00ca3f1398b5a112126fc3f0e2d11d40db36d0aa47d59cf8ece36df8daa854509e546295bebf92465f32a7edae6af4218b49781034329c22ce9e87258216b4a901b823a227e46f2938df336d3fe03fdbff4efc7b8ff8142b4bb60592abe508675d04081e5eee99468971fd49989eddf4648d3948266b08133b8c22ce3523757821a05f254cf5691848b1532655e85fece5eb186e87fea61936fa63aa047b5cb77c08aeeb2991c26619139b951ba4c2ed487703a6123c9c16d7b6cd3a17ad108fbb7c79fc3706f654be77a6cd8a5213d3976b1a4527144e788b712f37cbfd5f4ea5aa5fcc0f91b98d74ddcacb83fb33e039e1b88811a2138743ffc0dc5eae46c688f48c0be92a7094f92c2d4bae0c1dd95d40ffa113f27b8fd1808178be0c3bf77b677df2a17c9443fb6023c9e43c48614fd7d09a904e7a064125f8b7671fbb7529169138feed4bf3e6c215947efbf44fdb5eb1e3a5700fc8bcd1cf2a393ed438e68b607a1061e6cbd46a0b4f30fb4a492e786c5cc6d4f80542d64d0d271308ba072deaf7b1ff1c5200a2bb24c68aa2a5cd07052ee5760e0ede484e0a9c482cc74ab564654787e1698bef1386fe446106af25cf2fd950a78f4b521d547c83b6e4b6ef652444af342e90bb5761ea0dbf6b8c315996ed3d3a1a9d1e5f2b75ab18d0bcc87ee639299a248406d9deefecbb6883fe81ad12f20d80fc9b3c8962cd84e5da215b96bd457d5146aeb36d08185b2ee021fef7fdd43d0555c0fd16e7b66d1f534ae0b87f6299fc3b8e70236f39c91601564a5c2b3abce248ecd5450096e65df8b69de8b5cd963127f207f0df538f990bf29ff0c2930ab8b378b86d2619b150ab4dde82469e40e9e6e503182882b03442f92cf7449fc2c4b2f7ab91cf5ec57b8b0ebc18b27ad9125da57b6c139edbe7495defc73d68e71019b2b808a1ecb89fde58ed9dfce8b17e072170983e3dc3551149108582b95b2e4bd9552faff0bc7fd13dc0130f62a967761928dd62c9cae970ee447842a15baac31a280b443390bfefb7d4115e8d1c6458e1920485985635b75bf84b6af5226dd9b36c94bcd1a5f3ed99fc3e5adc6f3c4a592d2eaecce02d0e5ae97e6c4c7e101d4138e526fe3f090123c6df8596505b4d28c1be9e0c3f83dd4ac07946617eb553f7688f6724c8d8d34281a8c094740170280cec80b61b07eadb2c63427690ce73233f18593537e07266a71523688e6fdd36943d015c1bde058ebf5fcd12ea00e6b41565d062722941ca346b5949078421cf1f32fd38ce1be52fd65f2e9862bc5c57b45f33581eb72d387f0c212d68f85baf7e42058e41966429b0189da4eb4e58c1104e59d34eb0678a1385c38289210ac86f77cf8bef670b9ed04414875a29c27b79bc97e7febc40c39cc8e201ca56b25cd8c96f1a37ff688fd5d55cf5232d216283169b51cb41c631ca323056f2e8f74d5178a83b3bf43cfbbbb40f2680f338a89b294d4b611b0f275cc60e99bfcf270cb761c1c2a6773503a3c0e03b2a858d0cb5f81b032012bb0e15add080cecf19bedadc4d0c7e9d81fa75b97e7ca5e1c9eec88bcd10afc0d8e48f83aee498633bb260d731e0f900a9b0ae3f0d6e57346b8d3d8019bf079b4f909b6dce5718052416de8b61e9ef317ecf2faec5a64c0ce4bd5aa72b79360ff9b58d4a5f3e0648000d90a947933b0fe71d81ebaa96cd4c54c5d42b941e73423efb1f6626767f0e6dde52cd8796b0db31cedaad75b4ff72b83e0e9ece164c4f4dac170cca65e42db8b7ead0fa6e2f0b2d9576c79d1d73aa4b7ce4b954958dea381a65d4287bb1e02b7b88b33b2f1872d92ec6bf79fbb726aa436434fc7f2e79f0ff8bf243e19c08f680a5012c3c3d0501abb46026c420e485166fff457412a075086266b854aeaf5ac3a300ff5501bfd7ae96ebe56a5df5b7abb5fe2d9bf7ffc0005b6aae9dca8380a66c8c14e884f9f3338346ef1daad62042d09cd774b43016b0339aa208c5d6c2f7373797202ffe680edce71d684030e5841edd57cacb0cd596c63515a1b992de113fd2c8330840915d3409f608c8d43601f09dddeda6c504352e4d8ed932d8b5076a557fa2d87914ccbb0fed61c1f4d9950be0900b2212a82abcc4d9f4466d1f5469939c1e35dcd2d124623e2e1aa2dbe90ce09b958edc4e60d7e7502ed16a8a1d08dfcef84eed8a93d6940441ad41e493ec09abce869a8de0a778fd8df55cd1d9d7f053a72314e786088abdf968a3b1d1427e52427c2313182f51b5556466960017468a35ed2594280ad8493a5d3503ee4c479fd4c8db0141fb6b77331b46fb228f94e213ad372c09ebfc8bbcc79eb074162b4694b7bfb8444a4938a80747d2c15879e28e3cf44a8ac2ef8c5d9f93e9cb42f81cf88035dab96b6c078bceb4fb9cf293dc964b0cc9b722a84f3423bd4fbc57ad1f68a8166e86da595fb3b7a4dd87190dc65756e0f7750b68e3f605def2821619fb4fada9cfd1c41343dd4f5424a1911ca709ca745962a484d981db73a23a85c046a3eb73cb48b085d3286738e689437cd4976cbd3e2c5a4a4e385b3aadd09b08859bcf5506164db33d414caee679c65069543f1db8b242dc424af5a45bcb5ef9b939ce94e4dfe56ca25702d7912c38dc44d94670d90109ab685ccd59988bb7e932ffebc882b7dab274065578c041591d481501797b4039da17684a7242622f4390544b05023d3902949d348a44d9ff421d04a55395cb4248c7cb0f13b697f1423be1342d31fa9b85aab70c16bc62eae5660256d2472dc908768ccb7ff02cc84ffc6059752499711338e41d37f8751d84eec47a51c7daaa273e19c523d3d97ee5f71571def6f7268f24afaae71eafc84986eb8a0bdd50fa553c815939a4b56380b22b650fbb989b525d2862c5d420c83509b5c76b1a9a2105b45c2182331ce72e8f1804efee93b1929338f" + }, + "rotation" : { + "falcon-512" : "0x39f115ea80ab1e9d88aa542ea3b547b6c7ce5e1a0d149a3d6e9254322219553a87ea2ffcfef8b8900dc456a4265d22a81f8ba45edae8b52288b665ce560845f42beeb7194bcc265815e1984a7c4ad9029647d54b124f52b4e814fff6d1fe933730fa52e14fa28c6f8bcb4ca79dc6117da3eb5177779c5915f729898de2d7374260c8219547f1119a9d3c0bcadabd8d5237b4887b65b23b0d197ce1f8f874df9bad08f27a86fca319a4710be8243245af2a997b1e8cee6938470c21238118ab8e4fcd259622e928b86a5343110209a77680c7489d48121ac430a5a6912c60d57719624f3b997481eb8ea664595635cb7ee20b9ccc59f2a62b91ca55b36d22196d3da9f4fa6963dc12991ea11529f883795fb6556e5c9c9ad8e3489ae8d6e57482ad4a1634e92de9fb8d6aed2a90a5a21341f7a756840230a54eb37e76b289376dbc5ac29383612a5528211d0b2b058469ff96436c691944d722ec401af9e32db94013569f5ad2049f07de42d174e524e09329757586c64633f4bb8abfc38cccdfcebc1aa496f9f4af6304eb6d25ca571fe5a24fed2b720194dda6171a0433e1c252e6a7e6e4698eea1b406a4956d1054a500b0ca79d2dd443ecb4dd33fd77963e6d5cfc5376f973590a204d2a492fcc472eb85c4ae7b59112a9fa173b51772e5f7909181f0a1e81146d92048e3d3669de61502948b9d9cf2ff3af2a503788db5d06617a7b9fbdb90025b9c6a0c1a9b60fc5ed512d8ccb7551e33e84b30a8621f8ce4b379450384c652b5f2e31db8bd3a645b26bb2a2babd89c5d61141576265fa4f8198702e2ee2e0e7e6fe1bc7b2086da34d6a00120c91849b1df684dd623085bfd0da56820bef4ab36f27391becced9a82559c1e6eae8d3f4da7d864cf8d1cae4aa44bf37590e777e70cae2d63ed67e7b", + "slh-dsa-sha2-128s" : "0xa53ea1956641fe8e10ca42ce4bb87600051834dab4a141d128620fca7fe2c64c2a2a21ada2d9cfeebf5c7ad6d633a405ef6da081433e4f2b173807b7b89a8074de4d6ead4d960d161ce71f31e2e806f0bb0e82ce5a345fa84f6e91cf4df73030c6899feceab88a7a4a62784e855c11581fa66afe6f75e3534a9eafd96cfb3dc0964207607227029d8cc30e1d562ae043eca30ed796cdd7495a8281d04608a388c7f0022dd050a5cb2d17118c07c678b7fef93d02cfb3417498a0b22551ae3789ebc40738930fcb5304f94d65488135f8e636fe541426b03a7c86d4c07bf75dd40c78147b0be6df6040735679f3d5f24d4dfcb2e44da06a730fac46b68cc9025b31bfbadada47102511b94fa735fc8b330ac237448acdcc393da40a6303a3890c3ee0817c23a916a8a328550a120ef1eeb7679ab69ae8cd2070968ae8f8a83a1fb597d7bf777cdd54dff3b34393d5c5b8fe1fa0886b7f7a2bab18ed6593024117c357708eb964b66f163a3fa4b23b554ae3ab58af3743bf714d431c4377106e4a81c4fd23453d6b195bd00dc0bd2cc209428822051c3c58d29ebdf70206e2f116388565b0e32749193affd7e4de644d3b69d4626c8bfde76c75b200c5e0f7fa575e3f867b9bc2f370f37b7662ae2e5c4d86a34ff776cd83f3fc811c1b4db609ad79055cf3abe0cfcc4dda542750f50c1f40a0c5825e017c6e9818be0f0c468b486fd42b8d644ede762a8ab55436dde489f267c7f9e3a565a338270b94c76bde1f453eb0aa4934e6e45f9d7b7004f703e7cd3622dc2d4851ee71e87b2cdc952b3f07d8e28b53e05ad33f09efeb1223373a72380e28c5cb55cf16d18625a8defde8841ce878a0cd8c0aba2ba4613104b68a317910ee52a3f119a8c0835db660ef763a152fd7a9d12adb28572a24f1b9f9a2bab635fef204afc20a686673a471b443f261c9a45ebb64fe23c11dff5f245081dbd5c3ce77f2a9769c96853aedeb3d251697c7ccd5cfa5715c308b05b0e1149edf10e7e62aece96d12dcf818936ec9484b1b4f98ff97c09d16b75e4ad9e6ac8b11e912dcd4bf4163bb9d75cba3317a469cc6b7fdd99146ca182fa81291da9d005a5ba4b794e435b27d491998143f615eaea26f2ca9408cf5fb1ac3633556471bc01c7e5124c0dda64b8c9371bf9cbbc9324fff2761202acaf196529e1294725c9062612626ce5f7020490cf0e9ea90fecc1290dfbd1a5dd1eaeb4fd12e3e7844101e6aecf9a8c8f84745dc6419bf9edeff3045e225732da8bf56ab301581d7f7bc54eb36edfeab87cefa342df02bed2fbcb20dfb37e258424cc0beaecd37806430648464f8fba3f801fb16ff25d9b2c9acc6e4f4edfac10b6f37bd6733ea64d145c84979403a2591281609d0f9a06b35424a01ffbb5c8b5abea5fe29d553ab3d611fd83a2b6cf2dc7986e8b227874a45fed0573c8b48230a0a22e32097657c7dc188e97a7c258bec3e058254132119d68784e28c6a58ac85e3e4b3cf66c4da8c25134039c0748e78bf074c89196585f288e0992796f14825eab121d81351949b366ea6268deb8183620236fe8b6734b78ce6210e439bd27df471d30cc5862277351166aaf481fd3a5c54947aa1d2ebe277157ad1a03cbb2d9751dc9a0efd508c72d661bd878ef3eb9d0b2e334cf1591db86515d91c02de7a05eedf41a677d382513fba69198be0366d76a035b372b183e9a26d3eeddf9bd225a8d03ef910ae58c4d8583d9ed898ac040bbf587a3542e11749b3e343b30e6579ca5717fc3648e5546e88705df59165ecaa2c7e966a5c85359363c3f7689eb9b8cd3e9f672f7c5fa2c9165f044887c620492f8cac4c7e4c6bece51bfcfb2666a69522eb2bc5bd3f30f529c7b139ee2df22e718c6812bfce08c38c863b1dcd977c6a9608c96cf2465c019ade69c76591429097ef54dc26977f2c2987c3c016c3b359b2ed7f174aef9f9703885cca4533d94e747fc41d44e67f0926c9124fd9b5931331466f6c15b4797a302db465f332a01bf3e7923286bb58d624dc67b918e7299fc4701fef78cc2ef5c1c7b44477a85ffd1e344ca95d795747853de95f73feb53e264004d74871cd46b79a1249f63c9d4d57524e637fec7fac85546d9855fd4f6483be332b47a93714f18ff0c06912f92363eb58876f9e23a912d1f86bee005b3763ad1e2b2ede5efcf5cde64ea8eeea8b5a8be2e716fd490ad5983a09fb02ba826bd8a0b9c4cc848d0777473caf70954d0d7dc0c4203a929687769cd22701a698552271ab45364eb6925443702e63bb24c85c1c0d421ddd3c61e78dadbf08b5a05754de1408798c1666b55ce6beed0e17f2fe3b8aef4d07352946df6b19dec49bac5ca5be611683e015e2445c24678a5b3e3592ec6e3420bae389928052cfad9fe9ba31da908c9617a04420e56ea54ab9d28444ad65f9ec8a2e69779bd5e2158925f6dc89fe431d0b174f4e1bf2629629ebabeccc008105cca3dd992273eb3fff1e153936a3f29ba376820966eb81aecae4d4a392732c4259ad08bdea412f9669425f79e645056db58b43233afc2766d9872328d5f6693b4caa05cafb1d09f72d1a4febcdc07589bf58a1cf541ae1befa27c5e755b2ab16d2ebabc8ba0e2c53c019b98c21a93b81eaff42f080fb460478494bce14a43b12aeffd3cda505f388a64f2d7c199ff36c0d9f974680ab2619493dd3777eddf718658014215cc06eab5ebc7440321d60266d224f8a8e3a11188071aa9879c6b2d235ce086c45738faecf7bf9b451d5d257a301f59b952750a6bb31d625e2013d5f9bcc802e6603975c38a7436ec90613d374dbe97f8f750d2f734640acd7928974835d7bbb40569a8affbf3be1662af0bc257ddd77b1c19409093de658b02e4ba9db9ec49b0406877a66d429589de6bc478e74b946e55b0dcbcf25936bc5d7f21ab9862b4aa1ac41f9e3ae3120ba6d88bba007c599b82e7bdb7b1e58f8538cb60af18d419311695b480f6ee3373bcad379acf155aabbae67d020b7f9828e38626ceac93462494d82c1972f9eb473175835bedb0047624ccb0817814ad1326238d2f6fecffa7a66f4871e47c12a678d4ce72fc0980c3fb355de325bb42f4507bf7c359162158ee2044edca11b3e6b14a2c57376078d6a539453b31380a1a2b42c977e0f1a7412fbfdff53d817cff7b6ff9ae1df1c6a2e4143998d8ee70dd5791efb642c7df7feea73c877cc60418bf69ca32b340442dec14a482c9534acc1bbb981db0c2e3d24d516bba78411bb0888a9b29b44eec576ac6367216991cf89bd5c2c7cdd7dfe962a24be494606bc7dc974ba365c0be85be339aa03fcaf4e1a7a48e93c1ac6cd86e8af6becc6e1790c9bec9d3e127531d1e6cc30d6754a81f85214c13790df04dc5b94482dae16905882745a1464a84459cfc96fa9f4095d5b892407aa464fc1bcf0ea241c11237469df6051ed0621232385ad34ded98d7eb47a85297cadef9cf009f82788ddd13c1889102015114849c7454bcfea6b9870fb4d26adf3ab267df389acf6e87496aa216cd0d9502580884bbcbf46ff5d3038f2d1bd7a96f39369a7f85b0a4c433c47582b1bbfef049830b62df062db6beef32e1641295c392c28c34e73cef38b7b9e82215474a428e744b98c384d12065444c395e6e3f9fbbc7e902dd65b6a06715723944e16e59da6554d1af6ecf45bb41b9afbddd2e42a098c47e71b701e2551f753eca08e5496509319bd97ee98cd2609c4d6b998598b2ebd8c010dfcc9be7f29f3f195c5b55ccaf9af13300d9c8a22b33d85094038faf4a66112e9782b2ef19095ec73a79d8524a9d847e1ae6725b9a758467c1cddf9b9b9021289767e4c60a159167d2697ff6420454ae0727d855355f82afb67bc8675edae3a31e6fcb5140850667d3a23416c7c448f64938f068cab7ffa2b27735ccacf621a219924a53e69216d4f0f88ffa9e2afa4810f948a8159926886255dbe53a6275aaec7e66b2daea93ecf8a504334ccca6cb0d157b0e1cf8e2aaebd004b7edd54a8a43c6600fa77f067c3e6e7416ca70051ea1bdda14bab7fbaf5bb31f9871542185ee8476c7a79d200ab30825018e472c92ab4eb26b1de4119577c3443f274ab3bef4d2bbd19161c4c9818a36ec6670cf7345786105b83c5eb8215d0050ea184332a437a10aa6a340e07d34af8e54a6f2133ce65a4f724c08acf8c43ada3b4c79ccd5e86a3368e12d131632068c7c2ab59e1658897bf184c1eee536989ebd0cbc4eafe744451ff93a6de2274736e0819242262b695b5d468395ed212e356b617157026d2b9cac918081fa4c05c7f96a0d621992346592abaf018b8a7c6bb9ba48209512ee45b0bb5f2ea7e9f5ec0417d785b65abc0ae322c065ab7686b4b19afe7b96d55afedc9be68c9904407d7a0163b33004d1ebf5a1ddef1633143ee6be90654512885ce8f9d56223daa81db625ebbfecad98f389d34415cc70c58e9a4398aca7228639eb375589a8b8bc440999ab671c5993b9c9e7f3aae7d89298212ac0e29796b99ee8ff6432fc892524a84e0c86bedcd7c3665102f57604404c7eb002a74f5b5864b137ce326a9ade8ee00d7691d5e18d75c91e07c666cf8d32b3a066d4a094bb6d443b05e2172f1907f061d32d3f54db0156b3829d780ebe7530174201c8f82e0c9afe944e9211c2547bf39fe3ff1310a2816d75a3e15e37d5cb826fd41a3d7aed8b23252a41aaaabdf6941445cca1020fa172267fdc5c0415d20b3121ede3fd34988375cbc2d752a066efaa3baa2432a7eebb6793c3da39cd4ec5411150044de744fa7f7dd505b03625d9f9be9c4881d10b706f9dc67fc323f0aa2df04db2b5bbcfc91724a1eaa120369831f9d7ea0e31ae897c0f38afc79cf00141257e18598ba63ad92544e59b4c341bd7d64073180ceea8372a732d53d9f9641e7d290d06be30c8fd8a4b891b1eecdadae64e3f2bd94df87adbb9b4d5bd830f264e1d9d727cd48eb3af754e5b7d00035a93ccac36ec31f9a917548c7ba876656f6561d8002cd17521958c78df3604b3354a29e0cdbcc0377f0a936f81fe04e8cdbe850935036c28330702a39a07bff578a854e31ffa8f5ee282cafa1f088dd3714083c2c38999b2601883d02c083da105655c9adc62a429e883988638365bea25eba6ae7d006719551ce6126edb4750a38e3d78ca6e15357cca3265d036daea984d9dfe93e2fd6ae32e3ffb835b3d6103c806852509bf869a4a06d62e28a51eb1ddf06a83166509c8e138a7241e3750eb0ca4fe66b301fd537ba1d6d8a00e171a82792e5ba782f60c77586d1780fdad08cbd515d01e56cdc022d910b17c22d97f232bc16f5c44c4e28e2c2e14afafa49af61c4bf81714a15e6ad0d6cb3122ef288d4cdeeff8091322c57997dd6064a60748f7d686be7e0e877a5d9a00b06a11a6c436f9b0eae1a45175b3327640073f4a20030edcf2467550a43e2a808b37023b1dc9fbfb3762bd8c3f7705098273b62e6afff7bc8eb05b1a84047b306db8f54a1a48417223a68ba50f6514ffa915e3c0a879ca13bddfca5cf857f82c200109966edff38b092f7cbe07b8dc337ea8c77631f00e589cf86a4ceab5c6bef95bfca407d237ae7accb93a9844bb7eb76346ac1f2876e89051bbfa3fe829b65441e31bec66c813774d0f9877ef1d268305626babbd2bb01d3775ee7a3e86d92c4b96b367812b088fe2497df7ba4e30f0a63475a94343001f011b675d62ab72732914ba96610413a9c7761f35386947faa78dc71b9e3cfd3a1b0ccd7d86b66c02b3fd299608ff3d3429ea4acaebb02b11c9928784a558daa68cfef4c4b042576cd83ef204260231e1fd41946701d60dc124038a42e7112a8c0c776d3545f41d7f8107e86e09efa5de3db87f6df0dc28d9477497312536a3920d843c76125bc2fc2e64b62f2867bc4d51a733525a703730bb076da6b88e78626718af21813e113cc9b282e2c0440b6437c1825300826ca0e212a71c586339e726702c73178cbeb07a51751b28229f6b9ce5dcbba20632e05cd0860506daa291884cfdba3940abe879eef15441d0bda745e44813a403df52f3f43f0dae7a29252f7d41d49e75d0fd1c1fb60ce8d8c02838fbf1377e814a3c56d42dbdb9d4004069e886d548cdf9c14c2a0a96c346238cac08246718655b861d591533c71bf9d339781568fe473f770066caa5ef3c162d57a9885f97f02751994f79fd72e1b875132decc54f2c4e79ad1099cdcb7aa7c1b841f609dfd7da7b8a95aaae31955c28063d2ef4d9be8c9228eaad0b8690f43c776b1e9990b6affa797963a09e1cb0989b2af93c4e6c03076a4f42ba450fe2f9e1273365abcac6b560347d9747629581a7b618900acbc5bb258a4279d3c97754ca1de1b0c020fa3f54eab9ba30bbf5d68c356c660d053628887886660be12780497d786a940bab5dbfc47afcc19b050aa9ea5ab975b155afa0b32ca74e43e6d9f6702b033ec252d03acc4b23aa7c184cd6cd699174fc5ea2b3981b7205501427a5bcd6ed558b614b1b03edd4264bd9c6d12d792bf6c3739143e9ac09218b4bcb7ae03101e82147701ddb469f5ec87b8e4dc6f35b80b02e37a67a3d130813f6afe2006ec481f789c5b6e103ab55375eee8ebb3b5f4ca2028ea0b8dd5d76e3bd892a25d503d2d2c616c5b51f843ef173414b9c1178a623eaf87b9694d5c6676e702c89526c7e6ace13d1a406b99753c0df0c4124578c189a5911f5fad1ee13c85c4beea785a2c7d0ef08521de31ae4742e5f680b25e0c7d8d3457fb7a52d828014d5eca16ecada44bac06e75560a14161ff6401fa8d3312d4a903585fa940e0150fef558127b7ea4a81a099d629e82e7d8ecc4bafbea67e1746c94a458500b2024a6cee488afa305a7d997f5663fda179ccfb2c7a52d3c49c4303ce21832b1e8bb0bd1200d0a0750108da7a21e2a001f30f4691e5e6a2a5d833529316f3cd5fe573ef91d0aa6ef8ee0c230bb2d997a9b9de7bbd340dc6b2fc7d0d375f7284e511ea278f74af92f79c9821a26ea69e944d81b9da5cab728c1c3eb638fa69499a8d5ff8eee14ceab145df68ccdf49814c8c58adfa7a0942e5e126c7d1ca254dd5d2d88a7498e5b280aff9d76e7b6cd85ed81e03af2bdc710b75ae07bfaa09f11d493b53ea17f2a7c427def0056b0d0f235d792c8ee6af097d4358f4799d4ae39a596b095e19ad54a4aa19900ee5599f80675baf516d151034e92e0e032356098f3860e3ceee81234576e393fd8e0e115d813584f5764dde2aabf5326ec8279e3b8cd9a0b7aa08051fa1f6dcc814486e8b68b5ae13e999ec3b71b802b443934dc7d39c999fe3b31b47012b13cd95abd12283ad0fb615b3bd071dc6474e26fe0c1c67c099ec017f4ae703a77b645932d9f309c843b23f7f03b8b6bc818c3ce067036001ce72fe7d65dd454e3467792e8338c389749d80f37af2a54dbed9bd0993a942c88629249d5a7a94e8b8731aa44cb8cf0b3cb619e7a6e9414439fe03d157c64e06e851803703f78de4923c76367e409ebc7f6573e65f6dc1f8eac9922960e2bc318ef2989b2a87fd0182c0e11f5a7c6f50c68bfcc1e813001a7c7af84b0e104415cce4edefd565fcedbb7b5d2cfe3601d91d567d72ed385f34cd32a222b46864bf51fa77969247fe7df40325909c3b2bf7dbf7ea2d2bd873035be6de1cade7911b431ca33835a497fa588e75f36e026d040958aec12752d7b0d5e0b23d8087f4064b8cf57dde3385d43b0b419d147ccd179c4da58d026987504b358077af9528f9826b2afa5a544f9bd0f3f7b80774a47b1a67093e38725286f97066532bc86b444a8f7add918f9bbaaf7d90ff93c092647e137c8e798e714d62313ceffdb104010fe0c8feb379826bd12f7fa165def9682327265e4f5ac242517abae23602be183b568eb91c20ebecf4b31785d92ad00ef32ed72cb680c8439b0f31b2a5603c0183f28f7d59c16957e0315335d7babd3a7cf9e5b84d03b207f88b12265d8caff8ded8d8f69f7423b74acece7b0a77a6f153a28788ee017be1d47c08a3405cb172e2aa375a586b361ac28c8a8d167bab8dbd8f4207c97d6dc7b2b1ca6f4ce5b96cacfe8616a0af16efe67998fc46b0417f89e233f89cacabee5ea435d7fde8005ec2f7c0b95ef47594bf85fd1b8c56b478683d07ff30851096715e3d79af9942f609f31ae35a6dbf4ddc6256c33a69e65deaf0e30974c3439f7f512dfbb4c23a1c6bcf2e2e5c263d15af3adef79549fb3133bf232a13960d08ec5f64f04e9018cd8e1149b8b0f909ed24e155c29f6c39afc1006d38ca85bdd4715e745043bf0f4bf29a509e2282aff60e5947103645741c0daea3bdf7e330587d77d8d57967487eaf6ff6535bd6ff8746bd44171396093fe669f8f7b7d436ab1ca7e0bdfb1d75a2230ee6ac795c05110b909fe648dba2a6fe6c0dd7ae4a1c0011f48ded446208f88ef297b19ddd65b54e26886250c544dc3732358e43b6776df1dcbf2ed72e74d25830f4a61147e07507537159683113d7b8ea61cb0ff3eaa673381f8160d77d7cd142039d89e1bd957befd7ac7d41a2608aee47da6b97a42e9f169c347d7f4b309f4f51b3c47966b5c1ff5d0e368916a9d162a11f9f62daf90994ef05259ebb761d8d562f682552d0cce1fe4b3cde1bec80835705315d5df80b642f83836ecfa2da743555ceb7e06c7098c6694ef402f081b7323bbbe36f7ae231412b8a87916091742fd40ffad6c0453a30f69c45f3d026c3157a608666c776421afc525cc40570e61b7ef8a77d7dd59325da65b2beb1f9e2d3d95785366f34ff48a13c343db5cbaef44a3962816d0b4e09993e810078dd43cba3df88fe4efd28f5180991632f2c8bf83c0d9bf8cce723c42f6ed830c3899891e7e6e0c09c2a57c6307deffced4f91fc73aab9864cec18fa3b9d0c2f9595ab4d6fd5449ce44c81b169a18af80f0db8309c70168687057497699f66961e0c8fb5a7d5039fa251ec40e1f1249a89475093f607b19f354b4af78af2cc538116ae79f1d2aff8358fca0ccb270979a57109d20bad7b530cce37e4ba757f3673239becbf79749d6242dfc2c64006ce0d884a456d07a61d0e4c0fe396d544f6aba340b5d8163c99d230428cd4b3e46127e38786060edf6963c4196b61441e0d551d7dafbe19ee2bd28a53bf249c2064c64a4e60ba99ec4104ebe4909c36b8b6d36ad55e0dcbf257da83a775787254c4f0a0dcd563c6dedb2fbcdd56f11fb13cf9b90be8fd471a635ba85b36627ba2ce559c06e3cee092351f0867e6ad097fa73be4c8b3cc2af033768251a893a0169bd265c9aeb3dad7dba93063e5ae734c1a27ab1f2572b88c5302c1d1c059cd7fee85ef1f2dca147bb4477f5bad06ba4113ef70535af54972d3e140495820ade812a448205a157dd12c5b6a28b31c6d31740163da10c85894f6ee607b8b9a0346df5de0c5d5f420556dc158131d4d17ee5ed6881917de2c212809dbc4978b1ca69572d97e8cf3a773db80e911bfdbb0de20f9b439e026b88f2314c0f360add286857c11bd74e911b1b7cb46556c72b00facd4976ecc1fc45bcd5d261eb479f9666683a6d75cc3c693e15194fe902cc3673269ec9fde63fd787e32833520aa10c99478a8c1bd650cba3da7dc983cedf0051667a328cb343243e8ecac0da160de5858146c355b967402c2e96e3bac53e5ba8e5311bc16a22602146d2d39722fdb7db2e068d6b04fc46f76a92c869c0fe3bc132090847a72c4b434a0991afe6f294619d32c4c2a473d5c1202053fd23b6f21a103dbd45afe7a75c7248ee9d5c0c805290e8f259526e65904a18739cd87d55310caa18119a87098666f60e97d91c4b2ab71aff6e876c554d0ba5dc34a18f5e79dfa1d2b35da90cb53ff5158a91f3586ab0cb43634d02cf4e5a06dbe190f74de0b55b48f0c571eccb977ad01c0c352c455172ead2bd01ec28a489e80754bd7b2a3c07e46117e7f1354abf94f27341df0bafe7588e0a2d2735eef759b8d30efbba3d856a121cf8ae6a017d5d2e2223713875dd8e209aa0cfca3f660535e62b2b3d70a30ccdb471ba649f111f34f1a622d65a0aa5202c02498019a7664c48087e4c4e5941ea4cb3a9c1818d8663c0eb5e80479dfe3a7abffba62085ff7553f8e7a645cb479c570f0cdda505c0528b5ab1e9c0558f90c291e836b9548b4de93bfab2d97aa7f755e6f7bea85662c0b61ab0050a85adde2d514aaf6e5a938d4656917cb056ecc186ce4ec079196dd5179615e0ab1487b960c95254e98ea97b8248b382543975fd7e2fdfc48686e8e6d41b80faa92676d428e70b99ba78788f1b35a87a982a2ebc267561a91054d293fdb88964585d5212cca1a76acd00f418798faaab9bedcd339b3f39fe64aff41f3378aaec32b99948d2ac4845f9b574008ecfe8cfe01be788b7b7f30706d523f12b84adffa5e1ddef70582e27a6c32b058f2654b5dac30ae68cdbd57fa1b3983fe1c9083a3c03e73a452b4da00436d2bc8a6156baa14cb4f2e42818b3a763b000987a555903bc4b11f0e0f08e0ba99284462013766fc2379c1d93cca728183a970b712d9a9181d1b4b99cfe8d3dabb09a3c8321d94924b0b3ffd6199c5f07cbf9414fbc758661cf5654f9ea6bbf256992cacfe54ba9f2b2a7a39b2fdd5bd2613e8926ce4af2faf335d8f9e8e0ea25a4313b7ed01d71801f58037295c1862644b7b079d926d93670ee984857d323d5fad43916fd2d859b43713c7262ed85375959ea8fe01b8070441ab0fe48e551c67d10704db973ced0636dcfdc324401cb9f3a36a8d381adb5f2e0046999d26f157ea648abe0a1c2ab5ae0fa63b6404423c4d2d8d4326787f1b0eb375e26160f535b00973e795e27e35f27ed4f7c4683105fc6e2d655480abaa4ef403e1392603ff3b3a69058e2861eaede45effb4a6f27e55b01704cba7cd10fdfa574bee7293d06809e47d64bee751cd7c2de89d9c14a67a8f80e5047f3fb79aea411627a1cbaf56260d17e308caabcf653a4ec3fdfbdc13ceb919aba3822b1c0311b115c861290fc333c32aa74f38f7ee4c1fbc3f1ddff350c7393a48dcceb1e9ca" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a89834aba90aa88f605981578667395d730a408f3d112231997200c28865ec37a4a1f044c8817949cfc8a4fd9028832fc406f1bb919789f2099629c2c8d6bd128de4f51c02a73c49dbf17883ef90a9dbc0350aa67e024a893e555b4f989780a45320af4858421346445f66aa86e4954c0bce0debc896f21bc960e4fbd4eebed93e943a40387210ecda652c36b6da73570500adae1a2a03284dc47f1e370570a07007d8c5a64089e81e2c4d45eb6ca01530884070d46f4605ffe139457d5155c8e9ce8f201715ce7b9d18755304ddb6871551498985ab400cdc4349c6b5ed10b76b425e70376e39a6167622eb5d4b7cbd932aeeaca53b3e28ea06c44780b01684e66eed712559543cea677f1d9807e2ad0312c42e43d399302a0c4c506da7cba37000c3ecd10b58922889e5c8d31a9882ab8ae4b69d3d3d3abc6450881e9b79d9e2ac51085f61ddcfde73e57c1784a6a766ee2bc434d4973a9667c1c8e00eba0c544a36c593a764f519de83251ec48fb45a63e00ad4aaf01d1a295eda0eb95b27fc364c33e3482f4e5256cd622242840d0630194316033219c3051258f548a23c08d205a7b15199ba8d6507565d06a1f20db367183d56d9dcfd2e12c36bed83d239ddbda9efe8507451542942033282bce486f002a215c2ef599a6619247888a81782b005acb3d550988c1f22c6b186038bbfe1c583d165588e00c7f0f7e462a778e6a8126be71ee0dae7b5cfc6145aa6e65c8c136d34f7b45252652995898150cbf177f47ca8d16b583ce5b0e809567c08b99c06095c879b4d945b13c8a62429966347946b606dd78803a3c8113a431761dc868ba2e2541c66b39f30ee21b0ee3fba5eee922d56f49ea60c25a1094381908a69b351e325357d9b4b9e2d040d8be7795891ad46a334a057b0aebdb2e5a9cd760b02c774f60df240e05ed3708840b1663cb435adf9929c138b9fd15461d86328d5895d8778ada79d9b567386a6302b67b4b99a37f4362dea36fa2fa1f1ee2f4f8293b131c57595468aa5ba62a03dfadc9784c8190559887603c4b50b7487e795285705f07f90d8a9ca50e4abf0905d7487f04549132cb3a62ac2e490d6d5bfde20568290b693681218426fa0458eb356b198b41bc60505afdf7219ce5959124e4d1a56ba63345f91ab39afd970997895689c8397c023b1c8b774f92f0e5940c764abe3bd609a0d50a98dfe291e1677e6232896ccdea3d047f35c558daa130ff53cc66aa03ca5687a2006cb7c8c3f", + "slh-dsa-sha2-128s" : "0x3345627b00d77c13ce3ebdd12964c45d0bec4dcaea60b64fbf6cbe7b897b8136" + }, + "pop" : { + "falcon-512" : "0x394f68dbc8384822df8d4fd86c22e55256ce421178ceafde6e1a5f6437900ed2a86a72cd648af5e2ec15c79027aaf194847efd5855472b428c647a7db9d6e97dd15f70c3992480431ac6919e5b11c9154ea5dd9ac630f866c589a54b84a4c9fc57b86f2b5c835d3f6fac9de77d1f08abd507f514df6e7ba1c33518d3d1e447638eecc8a4b7cea388c2a57a6640fc599e73aedfb4ce6b3f147591b9bdd10fc7539fae4b2931eec267fbe70a5c6dd8b24bc98276db5d8da487bc38298d17aa7fa5facef7cddfdfd6eecbaf7e8086cab1df3c8f3be1913d30892aea7bd03d7ec1296247593835d8a74f467806259280a4e652f7e1bce11aaed31fbe2e155964f7c37243a59dad5c8d19c3c6b9d0f62d59ac39578dacf29d9ac2c0d5f160f7bece5902417ea82a0736bed8a4d078599134506a7b0f8a3ea8d6749fce0d749722f7c5198c340f53af2b4e1054fd97239109aea654bd2be0307a2d868570fc34f56a95ef5ecca1995681cbfd52a0e309e9db779ae71aa921796ba86a2aabdf6a39250bc71cdf8ad4d94fd6a758f29c90a7dc44839761f4a1a9c6d1905dd18d0532832867e48e91156ca67f2e2731bcc949d8140a850a8aaf9dfeeb9aa339b453bc906fe07f0a3e30f59a941d9f4f7b723a639e5e10f39cacce38936fa58e36746b0cbdece2a9122a110099104543f066515aa7750ae0080462d9254f58b5064a55b23a06dbff5875966598a7ef2417a8f32231c630c7b3d574584c2344e2fb38d8bd4d9437aecd68dc690b8f1e93f0109ee3747bae997419d1255a5c14fb1ed17b112ace324a41dc34a5ab81630fdb178641536da5268d928d19daa49507a5c81aca3e061b4b5c490310b13292385bb341b0550cfff7d8c72de94a6462defd1e6596b351ef32ca474b61c951ca5bc4f919e4", + "slh-dsa-sha2-128s" : "0x6e16547734391aae71d29745c3ac8cdfbc98f07ad9a8470650f0a0085883ff970643e21b67d6ffe83653a99f2198f821cc080dd7f0cd1a612ab5c6f18d9903ecce34a5cdf069ac66483448219c06b131b377900b999f9aecf637713feedd8c7d4beb03ee11bb297bf4a97959ac5c85c0c83b48147fa9a3be7543ce1050394e83ed745f9ec0c555328d3786da46acac3074c0611667ce5bb4613436dac210c2e9cb186e9540d7d43ceed7f278363811083368d50f6d258a46a32c8df5d62c42c4f6f34c627076169428e646774dd6dfe6924e2240e720fd4d53dee8f880c782862de1ff96fc5204a17625ca008781cb120f74caa37a36dd20136567c05e8e7723a2e21569f065d4fc631811ed0b2d9bb67d2c82b755d14cdeaccb797f207427d572419360af1c443fa68064a653b1e21a2b83fbd93e38c7cfa5046eda9b86c68f3855c61cee9f799ba02e611a1b7df6a15fdc4b4b09e51b3b5e1101d0dc90a213cfa0522e93808fc4ab3d8cef74d279b98069988f6b8c6b4c8d3d62fe2a5a62f1a7fb0fbac7fb36529dd85416a330f35a7e4e161757a83282ad50ddf92c6096cc1fe071952dae4540e0c9d0507d4b61ad34751ce17fdf2e99c3e54cb523d3ffe2cbe8a5c58b84bdde119175e9599df6b2666a7721e246305457cdb5ff5ab4ec9d04bca439d59989253bbfd9a91cb10a9fac3134d85ef28f1e4d4f884aa195216a4811be7617c0d6004c7833088851fb7758aa5aac838ddc5e3a51d122fbb03b0b5d0a3593b2b97eb69c2266eb236bcf3134414536ff36895bb081e9fdd2e5a9f9ba71310af0fa126eddac07ca67020ddd4caafc9b102afd4d0bcae2a957bd65d0a15762b98f4dc7b548cfb463ae10ec3da3b5a4e9976b8f05405904aff74d3d536f9a321b41e0c1d5e4fdbbf61b42d868602e112b53552b8199a6618723e5638273822fe0afc5e6fe068d117bd5af3a1c1e8bb4ea9ae639674b51a523d19f950629a76e9a52c673dd153915863d0ad166d357f8d23b85010aa0b8263de0f253a0ec29b0973af15e4419d965164b5fc6a0e782716d86bfeedfabf4d9705e7093373819a814a45b245cdd13a667b31f1724b5cbffe3b5ed3087118715a0ee68af333bf26f1ac1ff13eaad6a5edd5e760e89518e24a982af4f44386c6d92db7a2229bc9c4df23274cb0f10d348f2373a79f55ee05e3dcb256df464c5f3e54ab790f1fdc585cbacd93c775e50bd02f451b69972728bdce813b46742ebe9e45d45c8bc23d4b298b5956e03b48c0a11c602ccdebace8f92e2521cb346f291717cc1881b0fe3a2e23d4ac694b7f1cd296b32b7f2f139877a129079a524d7e3c4f9c36b5836346a6944c49967796d8564ac8d2ed5e804a14c655ef235fcac5a8cf0372f7df952af0de7e4319089b54ea3f718b539a9bca631ea0bcc9259c64626d252245d541d90179364072b081d70da0a7e561ba2902e8ce864a398dccf55a4f583df7b6f6ad6738813eef6815f8f67936b7b842ed7dcc48808156d236d70a9ecba41994bc0296c3a236f2772c747db6ad62723c4457bf8a2e526f306d8012e04adcff0015a88167d199029ecbd333a6ea35f5b9dc2eaf11a14fd09b35bb2842a4e610da4afef15f332d5d1e82c7b0b452b7564eb45193af060595975d496ee59151e3c0f1545ef2694eeb1a2e38d4e008c0943c8e7f181a258c35e5068df39376aad6ef356e0010303d9a0dbcab91dc708b8e9482f50aa18f88f45864d539dc0818148149aa259eeb270a474b0a117de91b6834672a5cb4b5fb7c2ea0ea7e3bc9884163887986aff1cd89c49a6ca417dff067da528dd33dcd1f7db96ed50820bd72dc41984ed5c767478b79d8f61cb7b417893a3a4451129525a915b1e999086e6db74ef627534015206953ed3a5e2bbc5c629b00f3f3b22a37519426c20b2a10c60087ea32fe606d81c6457a02bdcadd8d56360dbd278ced2d4cb9af64af4ec77989475882f3c1c0b9f9e0b6528b9834e34024dbbd64dc19fab35f3bfda66c086f4c0ff06a29e9fe881340d1c0719ef568e3f8f683282b963005e93620c01de327adc0af4ea7f968fa987cb8f38350ebbb3b91192ebb009f9fa85657e06cca6f71edd3d0dbcfafcc11b908cfc38a08ba288910fcb14f97746ac20ff6e6c43eb099d09a717ac03f5e0600f0bb611d6972626999f5661e048476aa999c83e3f476e77e7ae09c6fb7c343a196e1846ef061be75fd8b662932fdd2c7d8784c6e498d4d828c910eca00ac199957fb44d572d4bd2c599fa6c7c2a166d96aaf4959c561d1e9dacf502f76b050c836194fe1897d54a017944a973f49b2ea59b66c24d18f13048e093e83af249854da9fd6b4974b4db3e8e65684b3b6a0a11d942ad5bb667460dd98ab54319cc2ab704a7a57c1583b8289477f5557d2da684aecf66eec3a54da742e2d24a6935ffc4f439d4a8abc21921087df93da2bbdc3a0f61729b397faf12bfe59c85a6af75c9f4ce4af19239840b57c3a4ead6096f2910da23bdfd02ee4f3676897f475d625d0e9a9cc930116bb2327232dacbd0ff32cfaa40aed89024b9ccfacdb76bae77a87b569582442ad2230c8fb3e0dca3937dea5685c46aff9ae6e16438e0e9ea8ba299936a42d68967926edeb3ef955d95c5a0b74a13edd280bdcb462dfc2d82e7ec89377edf6760450e1ebc61f035fff661173de5fc80dc66b8e3d38c67f4ef8c25c2c5626dd2875e10d870f12b3f36f749eaa0c82e94b2f30686cda1da606e0c815a35fe6fd671915483e1b2b48fab000ae560dca0119be7a0a3dfb347ef95c7fcd9a78e76911cb5bd438a7e96562e437e32eb399782a9498ac992716bd9f3d7d4e35a1f1821f9008f547d171ce2b69b007b5e377a3244092396acf958932616bc5cb95059762f48468a94f9f897736b11bec2ce0af9ebaed9add43bf5c262106a96083ad967b7fcb127012b1b37970a6be14cd9088f6ea927a66a76c35e1320452848bf7496fadbc613b2e122e55634f0491397ee5d211d780971998244f303802d7630837d05f3dcc588d8ca32e02f16d2297aa577576ea5c1b90d6d330a18444dba772d7aa94d0bf2000d598a069d03e7873f114cab9174af2f28e257ef2c0e44925da7ccb593d6183fdd37a1abfd7107ce02c655e7966286b72d65394f312d72505803bd53621a08100a041210cb912d5aef260b84e39faa7b5d8a539b6392b32ce6287e1a619804677b3f69ac46fbec010e726f3eb3458433e68e6417ee375dc70c2b2ee16b2cd9b228d6a46f92ba66a8c5b08cbd284dd759478c9f690c5e1e16d6332b5a0b9aeee43ac8ce3eb0c3eb6940b61a65bf7c3348d8dd15302b8b7bf6b6257e074d0af3eda088f7c22e6778c8cfef1f15d9f86c76a4b05c9afa228bc4f116ee8adb959b4ed9afb2307f2252b10baa436795028629d05277ea2722ffc14d9a5fd56e6cd6f0ba218675df025e50586e46429f1c6028e305d22b5479dd045d98a93fa30de08bec1576ea692bfe6b657b4be65578fa2dfcc197212643ec9a1a8de5971c07023abd8767c528025281f9854145e3d9c47e01bd37dc3a35b76639e4716465b862b705ebaf674a64313641de333828d75c16d4f6ebc4193fc2c92e5a3373fc79dcdd8505a8ea7ef8be6ffcecbb2e80017b6fe401e49193ab31e67a56e8163cce767abc393608e7485f41983ce27b9b1de1a0281f237d685d401641f860f8f11d10805fbf0c7125deaf583458787c6f2850b0cc8c62053e069df241192c6af9018158c27e30574a9025af5af73bd956bc008efe84b31fd9a49c110b803947fc939faeb773cd3c33d1b6a96fca0c079f2eeb4e3ff768bbe1cbada9ad22c2d97928389e5f7145a346931e4138d66319f0154cdf3b28cc9bc04139f92e421efb522d0639f02935e228d6eacf3ccf0356b32c163dff6b597d51b64a13c8c93f60f150f445271489db644665ed1208c025b35dcebe82e73034561704c602d1b24e04c08e4ca8fab21c5136b6d4b7af05cf8ed62961b4bc1c2883154116a0ca8933e36042eb98d851be695d8c4de2c2530372c606c93bab254d6b00e59fdaaec98c56415da26ea8449061e12bd22743a1a0e0e1e117a4a9448d75b3a9dfe6f213460ca639632d404af60c764d91405391137ab9ec4e8519d11e3dcae18dfad0404ce14ea4dd36dc3ee24b7a15709410818ed0a42091a01ed9b7f39f7c87d60d2b3dcbba880001a519fb7de152ec06c73d35c08b3e375b9186752931b6108a4b84189fe07a7165e18357a94962dc2761f4eff0df829427646d2210b55915be710e2a1bb413df2055e7aa0b0b9a4067d35881f22c08e61b587a59ef82ed4df267afbcd4c62f22435167f2d7a4a939eaf28a6f1903c8c046b2d7719b7134834cd56964ba860e60ebb6fbf4605a11b52aac2d595cf3b6533fe2452b65dfa0fff71aff39124a88e3e02977d6063a8370f98e3cb8c80f35260a8541b9584b1d2bcb315b394d7641ca56539b4ca8bd9e4fbc594b0a3bf1ed678be2f9a18d31d610b55ee175514738717d47e4821150a799ebce655d0c75228c2160aa1936dd42f9923de2ac97d0dc3ea062d41d5beffdd1d69672b73490d6a5e14a655228bc39ffc8f1e862fd39dc950dfe648e0ff87807e543d02c40411ddfc6c1f8eed733750557c9f68d830c83af737c9d8c3ff0d23987a8e09e6293d707940eef4cc838365775046af35dd26acfbb4264a9ab84818a71c5a13d8e615c5675656671e5fc2e91b682e91eca76ebfeb1c09334e962496f583affefc56dc144873c79d4df77da394845fa29ff348007c8ee7f23f2944c121ee3177879e6938eb043a0abb75991b3e789571e3eff009cf089acee93612e97be85e1cd83da9ff5006c14151afa67fddbd9262667f98df52dfded25f28b36fa32ec662cd5a6b0f844030dbe1c87e9e2d435fca1a81045b8de5b8ccc411a839064bfd133731f55ee2ed2ee727d9382d130ced8a48942ea295bcf75fc74e24204b808956bfc171392f18554764aa80e75a539941d90a4afbce1f981f0c35bcfc78330ecf3d416d0683c7682cbca066a10c43273a0f85495cb4916ec0eee9952431a11717cadc95c7626436953fdd5fbfbf9ffe329159d1bd79499807796d75c458c3e3914d1f73f656e9fa273244615710c3eee84553a7ca0250e97a0b8895695bf86408d2f3cb2fbffffd62c8583af5b25b7e2a38e266c9623429f8d80ab8fdda6774df598f966b86ac228639af66cd0e3ee7ad7714b4ecb42876c31eb2608fab0c6ee26e97fca305aceeee80bbe22cb5e9819322aedff05cdb78b1fb3060ef18ce0385ae0fe6803b414aaf0b340eef94a7517e5cc3df4bd273f92120b8ce73281cab96821ddaab08b41b97708dbc7d0edc49cb362759905ce8d42743f83a0d75878596c4c5ca878b7aae6fee6ca7b20f2ee60ea773499a781b958f4353ceb078e5c04b0fef3e0112dd0e3a7ef19e4c63a202fdcb8ea56d15b2fb0deee4198a19c9aa59c1ef2b3de73f291445c248779eb5fc76953137adce5dff20f2198cc9e729be4adf385e0f25f67f68c04b4cc26987c133b987f477d89977621b8313d6d18a5ff6a76c73ed608f5e4adb860e2e8a6cdf237d4871a98f0c448668bb42f29ccad560fc1e571f8abb3c75665a65a212fe9d2c1c48eae9b92bd0a8b7b3d2fef99ffaf7942f5834533a592423d2cd66aa793e85bc8351876946a1e49e13ee374609932ec1af8d4d3f52e00f783368aa2bba882cc91c5fc6eeb657f3682dca8e9d63f319df3bd1cf52c511f600a6c815e4ad59426d9e1f32831dc5d8b0673c7c58e8bc1601d241bcb76c1306ddd5aea847dc903f376bc4d8e73a55f0a5ad331f57bee70533b29e6b8493d9f11ce4f27f9605f693c05487bd43882ebe8a1090a95abcfc186d74a3708ce3351ce7226183a21c08497dcf52727b7de08ad6a970b2c9f314bc7b33995ffc77a9a36ab46c7a05f61315d18b3f2217703be5753b4ee1670eba3af00e90a94664039aac93e89dcaa59d4566bf286f33c740feb763d0a385855f4410052c52abcfc972298ce03ddcb2e65dca251afbbb75c516ba77001dcaad8470f61e3391c1c68b1b7dfac9e50d53d2ad12876ba96419b39d7f94fd88b58ac1c123790bca733b12dadec6922384c6f3b7284334bed918acc06ecac7674428d56496e9d2e8f0175b3c7e384218380b6d59dc93541ac0a7419eb0ced7ca8c12a487fef9ea50280b7f7067a8855e784362e85703f6b848ddcd4bd31293baecda021fcaa7198b27d37900ac192d71de4e5124089bc1c8234fc455fe71171fc44494395b323fe3575ac720af5cff428326cca56ceb0c47e20e1432018a769f467405f5a8b71e15e0bd32f027640212d893678ec6fa16e5c00bf5c0eaf2de0333126ebba6b2306211cbc731a6baa8fc5a64a30141a8bde237c217161e384af3355b4a253d35766a55cc69be97abead95dc2ccdb4d243f64aa6d74330cbb2fc9fffa79fb6b6baf99e5ba9abd7966efc1586e72bd25f6563fbe125abec67f24e3510bcaa34c49499811ad2425c25593ff362c90c5c4fb3c4c45a4c63c46cae8cb1759ff2d82ac2726b60b4f094b6344191f3828c78d369956b7a28f650f63260c2dcdfa66d1df4731866efa6a335281d1cf2ce3635eb4294c8b2e1d4b7ced7b4b58a10d693e9f5a269b9d3451198735697af3a7b4b630b835e3b56e252f71f9aa6dcdfddfae13668bbbe3538d5aca766dbab6655c4aa906e0106640d21c30f1a241f4f5dedd526e8f579848f1b64808afb54bd2737a18d9a0e4a078705847b59a1ca4b2b554d03304daedca50726cd23326a6507393b2dff27921b5d7d322cb9901e0efce9bfd95039bb3bf6a99282f9dfb1e90289e986a657b3aa212cec6e611423eb3e4523f4723302b1b4ea5db2cc3fe3a7f0f3add38eaf228a6f0c02b9237e4078b438c01a0856d64053427af9f84eb04f022fd12986287b48cde73554b014f75a04d01f7d3fc6285347be643ce214b72c911e6c5360909ec590ffec8b4395fe271e97af852edd3801ccf04d3ed6684c66c67278cf331ac7c9b748e2dff377cafc7fc6732352245aabc9f5f25cc29d8f7f211a88a7aac750e488ad0304b7907bb063e29fccb16dd7ca0b78fc3962046a83f9c7138d49df3b765df47829e5f56e5155dba194d5128648b92e4f5c11bdc9232725faea4b87d6d940b9bd7cead9462736c7ef605336c40c9a053a000ac390b6340dc85a6905d35dc83c2d13786d233952ca8777b5981b761376f4ce2c5bec0587c2b28aecf39371b8899d227dd58c054aea7257dcfd838c32d6053362842a0baee7f967b6a4f5e1cac240896a8ec8fabbc4a551a55e674dc8cede1b9cee3874ff9fd3b91db433a12e78904aa3ec84d57a0169f1719297b467c46b278f671f74b8bb3c2c0cc2a093cf93439c1267e3be38b12e9b5881c27ab47bb01397c6470b80174ede3df34ee51ac116bad6fba58f8b8e2c7e0d1dde184460a5bce9e1d853d11ac017001fccfd5b767afb69691844e8327d39660c06b8916e918f5587f5abcd0f0bc45328ac01b1ace66405ed6c7a5080f5795f2ba0c0d9e5d05f462d89aaa230f240fa47c5c0de0b2099e93ae8c0f94a45f0513ce2797cddccb5216bad88333f6fec5805e0b9474a18ff562e78dfe325c1a6bb3648b41595690cab5ca9805eaa8e13526d036f4233f8181aa8efc76e14031d343bd5d9023fd6cd6b32947e173807883b6a964d398d3fdf9f34a44bc5fe41c5d72ffe5fedaa13fa1da68e19656ed70771529cfe822fc694be1445c79a041eff9753cf47a48ebe9f2c9f1546117b97a1c2bd6e375b957fe38cfcbb29f8eb726f18bc6131b1e27316064980e35d95fd913583b9735c6bfb03ec86e935024e60679a61d972b4aa296189fbc268552acb6cd890c68e50e325719d2f2c99b8105bc92fb582eafc1d618b4e88070e6d8df4924a1780f63ee216c0e937073725353b0c97acae72ebe521fb05fa78a4d6ecb7ba2b860190b6a5c340c83249919c26466c9cf4a9b774fc823141f65196df32edddad59fb05ba24738d81734004c42a7f92f5234b7f4fc0d605b032b08eda4cdfa3b03a3b805ecf5e4ad667a8cf399efb3c1d6822d9d5bb5eece6e08a515cd75c4485537eed6cbd55efb0a3d393d2cdcf44846f0c27cf6a664c70dbd54fd56b20bcc482de2eba84163375b1bccd43da63467557828b791dfb0a976628206e26e9eb10f1e9b9fc34571cc1a38f54909a1ee0b097e0f22f3ca58b56c631708e9baa52d7eb551bd327a6e0014fe98f04d7b0544fadc06c16a5652ea16b6c92a014bdeb2c070843152847293a0a925cf3c22bc9ac23464919ba6340cf233990690c94601876bc30bd5283f97cfdee757caee7b7b41fc265fb2ca9751e3b523e8498d665151d1c67624a084571f6c29973e624703d2e87f1ae5a5e0dbeeb96f46698223e063d10d85fd13fa0deb3891359844ad5e8100036d9b70b3b7831ae78c927a5e2c2ec62992cc424894ccd6f064e9ac307e6a2dd092deed179cf60b4bdc6c56c32a538d477d7917f7203f898659104ad5aab3e520a9f21d9c01b581cc968a68a77648cd95f66b5a1eb1b9d60b0a73c2eb05873f279c4ce43409853df2c8db254fd5b63af900c764ad4a0aeb4641e1a4e4089967e83603cef0317d06d1b990354c11b17c83c2a78cb9152615b9eed33e20f61a83179748ed4044a65edf285b155d41c735a883a481fa07c4941865b86d3cbd54c345d7933e32daa90a1291211191e0a3a5015f0f60d2ff07782606c164485aa469bc97f74dbf1345ea631bacb972be00547074759fab2b98a78cc43b7e4fee587e40e23d9f8ecaf03337d47d27bce633ccd97c0b18509afe35d54b1761a4ecbff6c44dba5402f3111eb671183b5e6bdcd69398056b980c35628f03214e0cb4ee5364e51de27843ad6c24b1e972889d54ed2a31d74000c32fc90b3c96dc3761c9649305c5f5e75f5b5ab7eaee213dbdda1039d4f3f450c77e6434752b74a55c42c98719836fd91fceb27be449d65c89a823218e882b987277622ce62fd0b46130760f10be6b49ec57e25cf6584a99433cedb1ecb1ce25cbf5e2b6eb716c12064f2cc45f4989c4c1b6c1ebd390c44195a16da16340d3a8588754ae676a990df1eaf04336b3252a5177ca3b3b2afd8e7555e92be6f6d7e64bea52c8d25adce3c580b5731574729107afe0e1025e6da2f2b05d6cc223778ac7de1202566098d43d0e4db6df8d242b57353cbadc0711030c6612cb92a9f9685c88856a0739e61d08c8fc5864fb9d8f41b27b43152ec15b3307e2c6f20e00254984e6284763acc977d9e16ffd4ca8be3ba7a289e3c5863371c132b8300f59d50f9efd344d1cf51c5ac64f29210740be2dfb720397941e2ba939e31c9ed563a674628b6ec3c8983be01e41eca61f869b920bf08db3255cc04a1f459653a4a3c204e6ec5496af1984f5e47caf56993bce1124bbfdb5d7081895a96170241307f000ed22ba92d5f5d920ab39d3ad1e288abb76ec3acbda6a7779be4c65e99bc7d7d5aa57d08ca1774e0fb146cd3a894beac12715bf322febeb256518addf26df507db3c79cb2883e6bb1e4716e185026cd760c76038d52a2e0695d76b3bf9da7df9f4dea5323f7dddc8ea1a0031851928e354e703651f7a21df505a4e7f9f7a8a398582aaa9f504510c0bc6f9af8942601bb3a6df3faf2e97c9df28268e1dd466857d413fa0bab4ff017f12554dd317455d6c716f9449c5273ffc959f147d7e0da0c2eab432b83f96f7d2de7b1ebc41eaf2128f8eb8f9c09c7b3fb4e95bb3424b216232406b81abd4c0afe11835a8425304376b08a4442c6eb0e88a51bc67a49b143bb35416c9ce613538483c091bf181d322efe10b2204aa0f0e01507a7cb809cb54b5cca5f44c20da32b2b7ec52098e53de672795b68a44ba667bccac558210307ac27bc5c203a2e6a264e7cf3028cb4ed41e29bc46985c1440187155b8bd737fbf9480749d62481d805463d75ec17c7ba83c60cc7020fe4b110ab1a248f6fbfda3e5b8a688a280f87a53ea7154311951c2001a7894eee171a64cba9b19a21e09540dc5ba85b1b2880efc39c8886dee72e64f045d00d99a90ffc81fc957ee65b7bb93e113717e7e7a275814404198f9e9d33f45335908f1ae917e0322b3d12c3d298ee814d22f8a57304faa93c18a14fdf53f05499239b66498512a6856ea194705521857a1724bc2925ba34f0e2707c84ee234c3436b4d3ab46cab863a28a011236e564ddbab3d49baf77be49ff2f5f2ca75252971a421aa2d4975aeacedba6cd3629efef8b2322b516c60ecca2541112b59d73b88177cd3be06163a463306cac2c0c1b11f84ab9b2fb6ab776bf0dc0fcb381975e4c1a0198b41bf6c1587bf6ca7272d8c722a581bce4494be5431cc5e3038ec33d843b9049b018fffa03cb3ff58e5da6bd6d49ffa6f776a5c4fee3cb258c4bf92e5e4079e38437f321e0c29d608cf5f69129e705e66a7036d67d43fd4a2919271fe07052fd30c049b793d2af0dec2431ea687077cb7a7a5badc21fcb0c2ac10bb37247207596d33d1c2232fc2a225acb9f30be7fe6da19514def9b595929c1c0c4b146099bcd980356b601ed0701299ca1a5547d62badd0475bc76fe51d0565b95b52a801f14e1f1ca62526cf8931f0706e3a478d20ce7eb1b1088454beac15277a72c90dd95f6146f961e41caccb2cddb2cd0680760731bb6db221944eab186425b0c2caedd840be5a2575d8dfd5810c4db5403d4491304130bbc564cb578d814d952706570e56641e8ce6232a2d4b18941c35301cd5c29211237c274dfb1aae379e25efdf827ff6e41cade1d57ed229a6ba76bf98de6592e5ae3ffcd481b53071e452a0b25f12919003474bf4ad2f36138d3d7c23c89b5b41a25c3622bf1dab6762cfa86a5bdeb6f2baa9cb3ea7dfdf9bdc700f4062ce8fdf477a17088bb2bf654e2eb9ab943472dadd73e3b63a4fdcf90fffa98983130e16d512b54aa0d4acbb1604d00fb2adc4a88e87d466237ee7b0fb33e5f384656a8759143db4c377e2cd264259" + }, + "rotation" : { + "falcon-512" : "0x3981b03586d7884dfe64c33f5b7b388f166ace48916b895ced22b34d428da6cb1bf0b0f292968d77e45343982d586dc6c556f8ac4afea15682ff6aa72683367a22398495006bf5ac0e830f10559bca255d2149a0740e61f05322d4acfe3a86645817823db5c1524f27b71c99374eaaf72ac0f59033a05ca55c563d1d2b58ccff25c58e779666306e18c1a9a4bcfdd7b47d34f68a96f3c18ab227b263566951271db2b7d6e20d9febc759c0bff4189c59f6add4708f3d8e532d58660cbc12ff5ab24a6525d98e4ff50f2a2b14f041e5cd6f0b1c4be47ae745ab7a59e8a220e5fc390c92ea8b085334d6cdb33fdaf1d7cf3c477367f96a54c46f9aa448cc53ae521bd73dd8c6a652f69373c3c7fb18af3211b3969ae922a3ad33babe49c88116a97125ae74780644f6c2d31dfc5987f550e2ad8c798d4f0f5a05b356dae5a73949b876f37355793ae6b0c6729ea2d70b12c74c2884d76844faa866ac685de8c74f0bb2f5ee9027d731aa55b2e9b1ef5a13dd33656699333004392ca27769c6a91d5b26f3ac3d65d74f9219d938f2b02f5fab2079e2ebe905915224480b53e2a89beebe3ed153b1bf1f8e296b6128388a5625ad48640cac171c30e689dc6593eb24b8f575e09438162d4ed215083142fd8df2f1a67770ba392d92a11fba399348aec782c16af5a41168b17eb6104fe43b49a7dbc01a4766fb962cd20eab637aa3e420a6f9aec4a2f9697ca26ceca1718843fa445abf3910cd92d877cba165d6bbf86aa5536f5ebbe450e62b06f2a3a8cb03dc5c198f5c6d90f5337789924ea67fbb0a07c8a06ff158d72f86daecaf9c96f12bc0b30a5e39c7e926bbe241f0ab69930aec2ec3ade0746f55fe3b18c536132cff870ac4f9bd0efe7d84df72fa1b6cd5334e8e2324e18da66ba979091080", + "slh-dsa-sha2-128s" : "0x563b16e5f93863fb9479ecda8e51da4c5e0b8c2ab218e682251dbdc02341a70a0bc0a8bfcd7fe676b4a88ed00ab7bad29a5226e6bec830015cc98a88e82d8907e4b66e4b39fab840bc60df099fa85f9e079a4afc0fb91c1ff5f8bc18679ec741add6e6a35870278fba0fb69e912c6dd01dbab121ae9568e4fb8d2be66f506b4094ad1e4a249a01fd55d6956780c0252f978308118de5a33edf8b1de9ecd158c4c1be63cc25aab2b1137868c2cc9e5c5bda02cb50e7a5ac5cbd6c3500c755bfad4f65bd913f3e8faf204a3851410354c9beaf9ac2b69ac726a1b61a9bf625e9bba4d3ece8511f6f2aeadf08fca28d772bc1511a4f3b659b0a8f3c6c8376531540a9866870faa1b3306ad1a8d26022a08bc2a90a4dae1a3b67491357c619df90d0b25d7329fba316d7e845f1c324f99a3f4dd60a0f58ee0f2f0d28efc4642ec3374bff548d20d150c31bebf560805bb61b8638fb8940def864a2e38402257a0fb446cee1e38e000bae954fbf1f4f26db7631bf01089f0d81d4bdab306e142bd1e1f356de670679e7d0f3713216064b14bb0f85a56e9b90dfbf0b1c81aa4968a855fd9d60cdf4ad35090e2dbdf8de9224d9fe53902bc02abb46bb568a0ad2db5a73673746ad968780621277519e39053252c9e8fb46d44fdcbbc973875ef2e3c991eb5ee02671cd1580d2ec9e8c8035638d2f5409b0343173a071b2a72a237f8bd4ccb5d080fe6510ba2b355b9d39b3ca12ae06f2766d634457455d72d519bbaccb2efd04a142c7ede443d0db7b44c2d5cd0a3a468932a9a5370853bc475c622bb5c8c52eba04325d20a396ed36e5d4a59e1c00861f3d0b0e08a9f778c70c7e8ceb448ca4a52f6e0bef75b34a5471a3bd668d5d693f370048c93657adb1e5bd768b5b38f6efe11f315b3e6e62358c28894ad8ff475f98584ae32ca9c56b39f4a9f3aece99d8e3ce5d2443441e183e479964a18d53aa867d07ab6f6d9d019160421f283678a0e7774820005020b98fc058076fbe1c65d188c4d5bdea7cf0a5d67fc39820c80726e72837bbccbe1c396a79ee29c0c23ef9871795f25afefc81ba91aedc3d6799a093a0c0d533e094d5f1fa60c6ef72a4122ed34f3ee5ba6992b419ca1de2a9585bbbecb2a705386096e929e7bff99ba7a5e33805288c8ac315f1679e7f055d1c63d3229a206736ec1f7585653c0bcdcb2974d9bcf9fb805a89aa2ff4f57e98194ddf8bed97e4c985313b6487ff4a4131bf0a3e86837eaf9caac911548f3a60a3e1bf04d6720b1e4d60685b6db4ccca66123d23e17f2f004354bff43c4d61ceff52748c197296ae3c8c6380baba17d00531bea8f4c1becf6eb6e9fd50c86bdbade0f4da4015f486f970a812c778fe413841f0171743044d6e77789f1bafd6abd4234aff922148f3dba40f3c194053713b9b9a3d7047bb849cb5a806740d9424288bea002261fddc12a6123fbfc3c8dfa26753e92e2f6fd14e4f1895ec36d90f572198427f65e92a1ccf92bed4f56cfaddd747cb2a0a6237325eefd74da45cb52df13c7e78e2c60bdc332d118fe51e3f17cb85edb9d5070a8addaea082767ad40a9778e46a61803c733d1f27e36a4d1810d4cc77757509622ba4464ebc3a2f8bc9c9e49a650450f0ea57619c179e94eae7dba9c66cd079f160398a0ed40f685bf1a405752f9b2820525dbf697e5c198905f09106dfcbca5a5bc62a72b2dc8b85e991871a8e4d7091fb097e0ef13c63f59a939c02b94c58ed9ef9cba75db728d88c81619c86d95710c5b51733a84b9a8cf068869464c35d27bfb1daaf140d65afdee9b7ccdde62d5d4e2d83ce670dc49ade7b85d1d8167893d0c68faa29d159ef95d4381bdc6a4234ead47a472e7f9740db9e1d8c5a4c66a84907a056d625b6f8e192a89b10f6d4b655e74ca6b01e6fbb2b187ed75b0da052713862e7cb3457df4202bb8df95fe309b8eb87a711767afb1a3003ace4cb01cf3de67cf13e24e6d166e763a8962ee7f47d5ae9a82b951362ff226ec3bfe12b188ab659d8fbdc38e0ac6d0a8c68c5ea7a273cde1b53d5063c5c9bb6b80e803261ba27bd88330cbe619a580f2554171173f046ad43e2e5be710517889ef08cc9ba4018b68edf3e843ce09d0cb226a2f84ef49b60e0cf97b70dcb84a353adc79bf8dd7682bb18ace2282b0d78ab1854bc7294e3889c8eac041dc31ddf581e9697256428c469fe3d2c68d6ab627bc9e8e730632c518837a25d4e89aebe1cddb015b57a88ac7c3c6a2f62f2aed77ad8e19edfed15551941e87b560f05b24b3f261230ef1c3566b8cf121271611632e111756ce0346ad9ff9ce3c8caa1796641f5e019420273243dc1ca62721a62c7f182a53eecf09acbf08afc58907814f0ef90675f634b6c4e565a157bf4acacab2c01e6cf62f0d47eb3ab93ad79f2d1661b1aff34065e6d7e39746b3d94d92670c7c78e04f16b63891675667b83fe689d18b348d640cd5dc302c0b7a149eb562f29ef317218d5958763685bf2d86f756853538a5e699bb3f0e58c1934892cacb9854e25211ac4dc4fb6eeb2f59d99d2dae281f2592f0ab8f1f4e59708751da251342bdd5864e7a9b563e12c74e7ea762fa2db1bb5cac5c2b02b680f27f7372e7bb7914403f267c0e5a8a8e7f59b4f28fd0254b570c158917245d976bf85419d8778a3f03c02f99f853185882ad19d3aeb2382185ae6ce0349ff3ab4b7b419c6647b0edf74588365339b4c22c54bae1522c82318225c44aff5341b13a09df32eeed978da9069fe9795cda0763c78ff688c1c456a550ff450bc29df105407859dfb13655d8e6063eabcfb209a9af22f4dabeba0e151563ee2a20605573c3e5bce979c420928688be7c5ca0c66d6c4760299fd0699a7b96be126bd16c8b57d2b3da3a31e44ccb85626c1a5845926564305db1575137fa505389c17c736d648c96f1f529b100123d7535cfd9e6636d167083b1f2f6462ff227116ceae90240515329fdfd232762aee1d9bbd50fbae995cc4c27f34cfe21c924a603564bd3443ae0c2a6a78599705cda7d8c1224adfaf1cb0f1b7cbf6b2f8e1d3e69e200947a8cbac5704613c041b9eec1a947a5a45a855651275c63bf96d0bc26c964cd283e5cc2e81f7b13db18d58fe5c31da90bcd5e03ccbd9b7cf4af67897e031a73f7661289907c57455bc2e77237d6959328445b2c666bfaa5073914220324c8ae9043548f2cb28167d6d22d919f6ef05c0b71704fbf0e6ef338290b043319013eba466edb620c14c27ec65c853d0f07e55d56e12295e7e0b2c7d0cd342a428580645511780193dcced94a2dadfaa90e789775f42a5220f13a7757b966219da9ebddbafa76281fb4e4f10fad3b2c59bc1279e7892cba7c78f0492b683d6ad565ce4cbcc8678cd7cc8db72b2111bfc5881042c1eecf57ecace2a45e83dd10b60deec32830a8edba51a31bd72abaa26e47a59d7db74612a52544f05e3989d93f0c25a3a3802d86f025afaad0c8f9383b02bbd671d5b308f7416cfd3caaabc7875241e5f13643022e2e80eada5895a48d2d4fb5dc190f197837463b6ea5d8cda125dce4e9a1c20dbdaeafd6d5c14220b49ecb149848c9714d41e9324226e6daff80949fc51cd0eda6290a75d757200fd1832fe20a3fcac026cc04bc75f91310e6959d417a1370cf88e132333439d5a71c9165ff1a6f1193b2335eacb42b5ba5540def54d6d0960fa1b37b655040b594c57ec6984b273c58228a9bde8614b8f6bcc874e7dbde109fbdc41684051fe05c4de5dc0bafee37fb2be501e36e4d8ea55864432cc554a01c4c202531edc612f41dfbd0c220ca5b603c7ed47e9be4145b1490161e31a2e6c82eace746e62bb8862a1b85d35af5286587c3ea6bb72444dad67abee707a9d583455181c0b246fd0c33d3522458ca4abceaae209ea5464d294837e7678150add9a37f3405a76baeceb0eef3f3fe7038980e96652c5e53d77b15dd8197f304bab443ca94d66f43d69f4c7ec1c7bdc57093893a67d20d89e587a9988e873457cef966d9270216e65f1eae2ad0d3bff25650d884ec197a69e5c76d39d77f7e5e1c623bd664645ed7df0b69e8dc3aa7e6260ed481ed07cce4405de34313561fa30bd3a78a7bed156fa773c0405cf20d4b0fd1cb60d58a2eb136dfac1e388def9e81a9d1f1f769be49d1627f206364c61c5a09d9d35d7ae05f8eab1d0cd633cc4d92fe1fb58ec4845c16e1578e80c0702471577e49dc9e106dc59c02b534f59b0c449a2ddca0f5590d4c34c5f877503094256f017c9a4a0ba613d136e07d7eeb3a4cc23121a934ee84249820bd64987ec55351d3602cc7afd1ba272557e531b3371b99df3528dec1f8d1d4eb6d49ddf94027b9390eae72f740ff649a4438448463778df7d5a0a5f764336a2efe509afef92610de6dbf22cf51381a3243171cfa9ca8caf6ac9f4e0ea6238cd7ef7bbd5fb06d3426aafe2bc94c8776f5ee74725b2f7f1e91b533ad9ebc626cfc5d2a6eee2c792602be3e838f4fe43e1851940a1e21d2c4fec3fd672b0c7d3e266bec169315a9960c7c433517484693894f71328b5738127fa6e51a6b0145f7316f5677581146028bfc4a6e3399037e6cd038cb993bd1e63b7c5c9020ddb57dd78da6b80b9593eb0252b499e66c0d3526ae41d19f9430ea7de2c8dcff825ab026e4bdc2da80340d14415f22a009bc7b325d615855a98b403a22939ddefe7d82f9b48814c09f4a121ed92575cfe32e04df38cd041fcaf0b1e572fc18992c182cc8ebe05969d90970ca0ee9b651fd65a4d002490f04639bfce86058cd1ed219457242c4c233e5a4b84cf3e62f45cf2f2e308b43ea17839640a7e93570a4ae3760ac79ee0a61290944fe47ad43c67f01be52d639c85b3b5b07a9aa98b2e7cb3dddb82d2a0edf88c38b4119bba29cf812970bf79629d295c2764a3d3c8b20743e939102f6584458ddd1da577b5667dfdbb350b596f565d64040eb16e6b00a1c76af2e6ccc18273c3075b5bad660017506a24b8fe728cfb6531c16c92f6726127e93e919bc4a018b29cab8b098ec0390106d37e73e14d647c00a723191ac447819457e1882e7930e147aa05c6a6648d073baf7e0a379cf4a079cc2aa7111ea0b81b6737a50e2a02c3e862fed198020f295b1f0a2fd42a19d69599f3ea4263b01d0899cf13509bc2f0490c78c69cc8182d009bab3a84d12287890ce43424ec7fbbfd0daa76f08fb3e0bfee27688ef5ffcc366701539cc87446ae2a0d910664626992cfb8cef95990e59ab088db3cf5d8303820ab67a3f463c8a38c054e1791c1d07e9f2e14ee8d1138ba4b2f9763fb9faeac3679a777c60048341a9c3f370b855d4125e9597db28248e3d38313d18e5ead92def8d04f7b286c4f05064a0137a1821f5e5da38e69297b9fce7ee55b595ea05272509dd1dc96fb28e35547e2179808d333aa357fe87dabe64cf1c3fd4dd75bf30b9bc7d4135d3432df069f5b60e152b287e3be28f552aa867b58e47574cbc5facfee6dd2f585356bfffdf2abfbb13cc957a56b0e47225b9df42c4bc44f7da0cee1b64d4bea4d7142dc18851a86eba8ca3a632820088d1d0f277612181d33e66554a538228cfbb163174114fe8b5b143edee891d6cfc697b0dc486b384db8cbd20d1b48d43d254a5174adfa368a793f060c922a59bf309954546648d174ad56f045f9712d2a064ab37b05f92b71f3e11cee5d6709e8c79b501b103db8261574533fcde6bc4c26b257ec4f0a3a237b9ef488e17ad5af95fcd176452d1b7e20106a2119508d9e74de932ad79c4c608f1ac76e95b9ffbf9637cc0267b8ced6d139103528fde29e1df10e5adf50882053986d1670d420bcaa3365fb662e0d433508db556ca2a5913eba6cdef737e856c2a24be7007aed60ed10fb77b7e9b339a1c7da723b3ffc12fa12a012cac3b0be1e4a317b17247b5f1102e006c745fda650ab1ace51adbf3bed3c15281ff975e2e7cc58b7998ae0dfde1e0c1376343d0dd0aadd07d8919c296a7cbd4c8265ab3e0a4f5c1f6da51d946f9bb2b00bcbd134335691ff4e797e9d07779493d88e1e91b033479207e36e76f1d4264f551ee2a9a95c33f4c588e5f3d366e2f187d8eb77ebb3f9acd757d7384aadd90392aac2b230804502cfaafaf2328420a41c173d903996cdd87366a33efdf9d565cb0ac0078b2f40cdbf773b86ea3f44eff5b86e822b536dcd72006123a4e11b26d21120d8b250b8dec716b091b1eb150857bc64759f31d7add4b058841193c58417c33e9238f4741804f9f148096b15ce8cec22dd2c4636a39e86eb1325dfa7ac87e58d34950a309b4c513a7e14f00cb5c8b6bbbf3487b3bf9f29ce445a9cdcd1eb402724b2f092ea52ffcc3b24a482a24d19f89d44afc6e166082bcebafd24d54bb5022a08a2469e1633edd7832e6c74e5d9976beebb6928620c9e865991d093313f4f24d7e8d7e63bce3e4bc8ef79be24f2b7278dd84e80801d304cf3d775e53e8f670fb0ad4dada5fca06536f31ab452a7d1dbfa032c2bed87b66d0a7fcce064a4a28b60a7e626605385b44fd54286ce871212268ab65415f69ad0f60062f8f7e46702d5f54895d9b2e7c219c30f1a32e061fcbb5875af955f73f2bb31db7b282aa12dd0fba2c8e469aacc6914c230d499c8b7ba92572b19dfbc2ebaf4ebec1842ec54ac05d95cca796beb8cf14f5fbdd8e4baaf2d59833b5a329e4fc3f32c730d5fe55124b1a7e4fb7f64f63f74cf928552e4b5eb1f2976a99198925c0ccf5ba8ecaec313d216e7adfb1fcca3693b9e74b326551a0528c5097e1f34247b5eda7bbacbeb21fe6491ce67c3c7764cd798b1fa813454bda9e017b1c8981d95b3e65782f89514d93a0ae33ed10e6c8897a3cdb9d01cd0e0e12f3b398ec72409a4adcb18c7f6091823079782cd68ac017edd272464805f3e831e06f02ad1e1da0acb7cd6807a9b80c29fa3b2ae5b1b75b43425041bc26cffd0c9bd4372925f3ebca751480063084a888fc9c0bb5243dfda4b4a9d4f8dae59f4eeb768788e4db8a601082ffd3e09d9336d5f339a825676c3e3367b161ceb3a6cdd80ce650fa711452e886b1aab4f4a92f07141ea3c28bbffce0bbc4b4587d4cbe42b3dc7d92d0d3298dc751aa71ff7aaf353a75ae3f23363cd925247660562bfd43348a7fd2fe33b2c46196a0b7f30ae843f32c3141e7cd523963162eaa726a8ba34b6f34d8980d8a69df2a103e0462d9d57faffd6c1bb3967b877b9e319ad357fa05c25c27c823025f246086de0f60d668930a5ffc304a026dd3c06a1ca2ff6328dc9046c28664087cab513358de893da3fadb740618b5e0086ae17aa8ae59c2a31592e21287e600fd14a4c57b285991398298944729029d979811d3784484e2d09a883636be1cebad5ae042187b62fb858e47ee7b66eb0303c3ad215f3f1877e6298bd439d10dfe2055655c68527efe9fb5188a3345959a36d49e3fa5f05d1bf880cba1827de4be26ff650daa1f0a4727383b2601168996303c550cb8ba0fed5d739905adf7ca5655d34a953cd6a6f47110fe9330b831bfdb79213a898f1e1e719786407e10bea78527c94799458c64706afc3931426326bbcf2490db4fc85960da9431b9028eac16664559c32e55ceb99c020126d65943eb4eaacf0fd642a1fdc2885a749a53e27eec2dd4927ca58cef7be2fddbe7e3c6a9b7f8a1a047e1720b36f92cee110c5cd35fe4e5fd88902feccb964953059a00c783d7f8e480955bdaf448170d2003e14ed223c2b9938744e1c14a619bd67c306df9bb00620cb300882c2c48de11e512fe4e249ba69aa527cd718faae2989b9306144e63ff75543e2a8ebaa92b19754a32ef4d4174c8c25648aec0336af2ea17f85096dce5affd0a6da6079cfa04c1acadda7f91f1a15f4d2fae5b3516a69c4bc0664066ba697de2de4ef233143119f5ff216c68d9a37b6ac5d24f334851c147657f0de1926c1b026b7028d54578e46383b4d83feb0fd1e366e682aedd5eef12b35ff8e192f82d59db1e82adaef5f0e3138d5b408e5948048df66f20501013b5cf09755484236c6e20e98e9deadc7c6280c463c002c274243b85fbf8434200f8f6621e754a8b1dca5a366bbab3fed0e22cb235ee6d8756908fc49a72cae7800e9cbbde623963e12a1b1c01e2129d65ff53d6e363da80b47323d15cfe7f45bb9f3c1619c59053584cc4d68d586c35540a907d590b7e076176609687af7f06f811c6393436ec08767fbdfe08145165869dca0ad295de1d4b170bb30b0184e92f40b3b0225aefee2ff8d3a9a94827bb185d48bb9dbeb2169a377b9a9ea40666eb61009a41756c7190cff6db52152119e97c919f9a327bce47b0c8ed157207cadaecd89375155c9ede0ec95dc336e038cfcc3fa6a14ec85371cff3a5e28086437c5b4f343faf76f77980f2f35d9592b9549eb94cc402855d1a88bf04a67484e9a7b7425179ffc5d5f8eb6474dd76d5f64c7cdad8e002d65bf34b9a7f597a515e47060a21ec853eba88bb8152ba22e682ece98da7b90b085a80dceab5920668cbebed0acaee891d7b16c5a5075fc007a63f954a5b8ecad07ef54ea9ae6d67305591f6042fa1fc5d092300da6c97492653d2a4b1fc3611f9e7222451ebf33a836d153869433466957717d5a88abd01f94783102efd01116d4f25a5a64d8ad5946f4e798e9c4cf7ce615aeb0bda6f7c9deb632f3be4283fe8e7a45a920e15824b8f354144b71dc580fc6c1d5eb5768417eb99149a177f07dd61216fb1f85e100bf2e8c5c4d73055d19ce09e970085d2213617b738ebeebfb386b0765e2a5d0de3cf7cf147859cd9b5e28e388f2b9d1de6987fdce0fcd20ecc563331290fe28a4e1de9c327829c4c69bd18745a8ac8f9a0a8e4ad65b07495a63b20f4b2f4c8dd49cc43d9474072781ca60637837eef9a2c9596701b607aefd8c679a3c714fcd9a85f73108c87662a37376f67e623bebe30995f02855d216a4493315ef3ebe9160d9ee0c05cad991f7f42def8d662a144d5222a1f3cdcffc0ec111b3ea555de6726fbc4d0893bc87ddff378628049f1fba03647d13ee4f98815843c619b9f02faed2d3fe3da8bcd4e839ed749e3c5834597ce1e2892ed0d443d8d983aeb8fc69ad80184e69791cfeb634b98949a17ceabbf7cb2e85f7d3d771d5c9a23f327c4ebf8ad8495830d2f2f43b0a500476c5a37ceb9d475a23f15c69e07d03129265dd70a83ce8a5cd40e929c2494830897e41c62de0ff8c8dc9afdff4844b28d4acc376abf59dbb0d08a2f01e7b622c702dc514bc277a1129619cfe00cdec4bc9a60383f43b79587efd3eb9bba9637641068430ff7e7c62ce572f1679962ddc524d200c07d427e6db62657ddf872200751be788d462723d1712e06017f6fec18ecd978ad377d8db8661478d2cf60847baa34dc43e1910482d42c02c1eabf8f3a3fa7fa8566dc61756c246c44a2bbdc6378ffd6dc8e35242a057c7b2751d8f537f6596426be24bf89cec38c7133fd1ee024e53321bd787aa1d253b608e52328240618da5d45af03710cd1613211f87768ab518bc4f1cd01254cce9c151b2ab653c1451c72b9530142fff1bd6f409d8f4b4cb2c589b7bd65a73aac8980d9dc807856e37edb7c31e27fc7eb812d5c39d8f95ef26626ba580b03390c180fbdfb2a3ab2d6fb563c51f49e9a10f25f46289e74981e019e02775918fbe9a8cef65586cac7b9020bfed88ff27d60174f73570053b1bfda473050b015427b14d561f773c2f5b4ff4e7eca0e4e7177e811e6eb60bd72b1c0459d1ef6615e5038d7fe06847ab24a7f41d1593acb4b54d8c2fddb6ef23d62b0246cea17a1cb2dabaeda2625b1a64b9da3b88496349c41b33935e39700913b23ca60b565d961442cc8c7a8991afe7734965774eed9fd267fa62ecec64f32792d9d0fb19d8e666d852466da5a73ce68275785b983d16ff3849ade8adfa672743d923858e886fb344f653a0247d40c7b0b87a7469c48d6056c340eb8d7e2a04b90930801424c6b9b2547486595f505a89e1492d015e082957168fb103d11bab79cc4852638193a855fbf35294ac690bd295f6fbc28aaf194e13e4b76c6d05772a6b2f8513ce2c03d884fad0df46228e19b940398ffb09c3d5300b4ece8e71bb25485d0d44bdbfee6655bec314759e46e8b826b98692b31bfe2c817e4f5d6cc87e4964770adc9c60178e5590b31e9880c80ac9125a972b04f4e24b1eaad78a691f70823dea467f61a7b308f8191f2b8b7c006e92d950ca5b8238f20ef75c28f81180ce3ae09e72db1700740e7a252ca957ad52c4a74eb8207d3eb9d053febbe48b8640585a2106857144a1c2dc3d12af55a97bd93db5dea31a28bb94a64a54947f9443c762eb8efe68499c9ead28cdeadc913828f00c82fc4bd96aa32ff31975daa9c61b4b6349496292d9b75a6f0012280eadd9853ee4d0130cc57b0d24a0b8115fb5241037e91b93142810ba2daeb6e2ce3647f367111082dbf7083854d395b47999026d112aaf90db3109aaecf7ce10e72ca13d5f6da166e9a938fe439230abb58abf47d72f39ea60436f0b8f939d76832abdf58f07d90776f29101b99c808d44ccc7fb60ce155cb3d2a38e5d4f215a7ff552cbcb64d964f1c9e32242bef9175437cd507daf55dee7e3d48f4c0c5799f2a692005c7783d11eb84a533e79a1dae908b2e8b8924b0c9819f190b3f8ce61bd3236b2d48025b2e707a2a64401a821603b79329d390c3a75aa4de23ecbcf7d90d32d6a42c160e02843ed80134c3c542e16791b0a60ac2d3df46e9079d3eb37b7d220513a636c9437945022c8b9305d90953d478bcb956c28556066200109faaccb090c4084ae2e8126bc01c9d07a7f6cc2407d0c4a56f11b2d69780ad0c6295cefa259169800019908cee9d52804c16fa34b4c5e7feae98aa5e1ce7c7365ba499fa1622bb5338b1fd951134e48e4bfabd055263bccb2f204a710dc531c7abc412c92cde71b096c9d4d2ebf98d33ff42c3a30699d14f111062a78b5090bd27f3cf3f6904d81d4cb8a19a924af0dc45253d370c5a1e7dce7a2d1457dbf59eba5e4a17545d1c86acaa4805e4aad" + } + }, + "2" : { + "id" : "0x41f547a71d1404f147606491f3b96e49f67d4658", + "keys" : { + "falcon-512" : "0x09b3f618864123704b78a9552a0b068f395444870c2cbd5d741a0c565554bc2785a691ff502d6ed0e14c925eaa24c075d20949d12f369848e178d50b085e87c9b251f68b61ec506435bf5b53a49d299409f54e906daed0c1a25a63563090f6660380321e786311b492bd7ff684a2650bec34d5bdf071c49f5a050415b2c3579b2994fbaa9a97b7f24a30e7e8bba68145196e0424471de31775aeca732dad506b16ad4f61a325136a6664f439e60687e764a009db3218f706785b98b154a625c168fa2ac541b8dce39682172b95e19424b98ba985622b2a23d8db2d46a7230bcb667372a924aa4d1a9e9cc1a8dc1ae11fe88081d74d857bf63e9814a08c9ce059a4b1ccab23a2fba765086b15bcba135d0937a064b90663ec69ab521746600610bec0a6b3c705277f90d588a00e908dd075059de6c52fe05fd8fa8ba1b3c2cf151cc810225db79aae0328b8ea1e92fc67b7bde405a1442128023128a5f7089722b954f15597ebba3aa3b55ae947be1e659be52af04c3205492387b51e72746a904ff13ae41964281ebd81783528269ccd6d2a7845d790fe4cda8ee58d93de3a3ca28bc4e6e18e812c313452a4468049a86ae39d5fe156fa2a43d29161bc911d515f4cb6b625f4679e37d5b55064a16c3e48a7f3ae10a09d9ab7f0ea4dfa2fba9da412af1978720fc47185c7ae16474833c8d278dca65b822c5526bd4da67c0700166cba95e3d23691e6e1e86ea4e560cddd59af18a689c4dc2b6dc0e114f2a84754498a8e299ac17a12fd199d1fd8d5c31b0a40ef4b2fc9627d16ec11241fddb4fee6c652d0119f7d60e852decd3489cdfb75c10a18f562154e542614427ee7cb0e96b99d74eb9585223760bac37de51697d86403ac877f823451d8c60e84188a8fa6804c3a8298f223169c4c61fe443434a76d6a51ccc1d06df3b962fdba9e27d7723a8390f4672d66dc2f756263ab928d9d14f6d550acb286647ed6dfb091c462cf4756194ebac9c94bdf7ae4ac7b5093ab9cb94455dad8ab15b134d31947df478a1aca5e28e98020dfdb6d4ab750911bb2690104b9606e78c8659c2f511bfa3254d32e65aab180ebb74f9848700a8cba3a1a546b77678a17716f232e57b204157761161670bf654f182a14bbbb1c16b14b652127241167e898e097b6270b062f0607a50bb040868768a24312a8da316dbc547237ace3a160b7835c805061f8f2723386929835251945908058905f625cd9a91eea83c0f693523128c68613038e", + "slh-dsa-sha2-128s" : "0x9fc24e59b10d63d59b6c448f79d029745c4f27a31903ad2a6f6fdd09b3abebb1" + }, + "pop" : { + "falcon-512" : "0x39916580739423a110970dbfa8b6f06ebfe827b6503b1629f4008d64024e63c71af7a932f3f29cbf08eae4cca92f36f670de2f3a0a461999f431bd5da16b868d2230d6403158010384f0f98d2b95936e0a132f5b86f5d11e32ecf3cf206ab9d26c1cb7fc93ea3468109a59163b15391795b76ce450813b28d28510767270c1c474632bd6eab70174350d329d0039ef8fa6739b2f665fff021cf669156e3797da72e5913d08c94a48a16d12b5ad7e521b730bef92d7cc277a92b3e4c9f9cfff7cb3896f93dbd495637c2a47835f2afde4222c8dd36d4d6d9cb419b5b5563bcc1a61b640e70bf2c4bed6599c5ec50643fa302c227ae56adc3d4b044926f4bc4b26d58d0d0778e2b8ec5e18b5cb9ecb6d7a26808962082bea23a932afefd0d483a9952f98ea11b629b0ad74c367e09cc016b817b624547ed8fdf360a8a06acf7b5f0d3c92448d9f7e9453a4e29222611490a7188dcd0a8568b67d333228454cd9cb99fdc9d6d945a64d8d96348120295b8f2ea9e95f92d9dddfa2a91b6ea2c44de7b605db8e3e1019dbe5cb464e0b211713cdcc32ef3631caccb48b6b0c968492a5f1288e99ada3a4d8287b5c9f46236b4b3fcfa55135f1ee4b13e3d6765866a316d8cb3ce80d67a518ee974976172f91eff1cf973631ade2a2b81134c39d0f3c72bf4fe546a7bc7d3f1142aaae3f5b4e8f3932d9b516ff1cfd827e7395d576d96cff52f00e76abe5ba3f1f0887fb064ee11096e52d441d1b4dc7a69462acb7296c489f498c456b2336f2f112391bd7e545629697bd096ab373a34147643199222b8f1b05a95bba5877aeed29c43bddecbc814a6f2bb38d1dde30c352e06f19bf52717236488848599eaa1ef17873681e91064430aa2f4a23445fac0a8c8231c8cd7c27f928ca0ebf7ab08feb53", + "slh-dsa-sha2-128s" : "0xe922917f6a5d9d49338e445586f4b3bfcdd92b89a013edba38e598aad56f8d10875fa62dddbce6c66865eb7d14067931d640a3b7c5dc448bf7cb6309943c2f0930c78c03fad2cdfa1c21689c6f621bfb1cba1968dd28e66ceb6cd5cd4f10b8797a53a8ad54e93febced4dc1c48fc4bf768b85a811866f86f004fdc340628ccf44b295fb5310898614b6a0341a2456b94930d086ee75f67bb935e62c739fb87407975f78f372ec11c0bf8bce5144abbb2678711850174134e6e5acc3b87f14022d158a57dcc7916a8e75341488af6b1d9c0af90bf2ea86440fd1b8001d7fdb8ecb8cf392498e41f8be5c7aaa81841aba7422c45fcc0caadd52d36d1d56cd02d027b6ed9d122ab3d53597507079bd9fb476105b12e3406c1873a8bf6a5b9479b5cab9c370b79425b1bc7616e2f1f66a398c2e38f7a9ff1077918fb3f7e65f025d4feb8b13397460a753b1999b3d351c5b77ece3751686c02db0ea4ff23727527cdaf038ff060a09689a13fb7dc902cb82844b11f8092625272d8888090e60cb1040b6cc5b0ceb6854a7fa769a77523e7a718a1a9cf56b94be12fe8fc4d319b335757c6b4807e17e9ffdbb486d9910404e675648d069c55e96894c7b2ab17d91aff88f44ad609142ea3747117596c98831338c616cea7439b1754390604188018818a5fd2cac7522b2df5e42c14e77b7383744234da5fe1a02a60e18a178751eb9c0212cd847317d64dabe4407c4a4d2ccb6965eb33699c37c733d42e66f92b01cf8b0ba2c8d737711439de358756ab4ba374e7359f8ec5838f531c984e2903026acbce89ff2935855ba5e5f3d85c82bfc3975ba3d4a9721ffaab1182035bb7526f062466895c5900f854fb52f5913591828cef55cd07d70c5805327cccb9822860f32a8e8f78806e39d36ea7be59186da6ab5f1739b6716323b9379446ce6e50345cfc9b769162ba96929e2cf0693b77a558e5386945fe5c6410a2594805695a4cb602fb25993a0359753f4ecea67d0a1045a80054664a5f589dd823d11b1e440813b3bf141f119c38f9595c1eec85ce74fd0cd69ab783ff7adf32746d3c4db1153df20ca1ff94acfcf3c8ece27b9a7819f7751028435730771a0d7eee4d5a05627e9577de7a1c0d16400a81c6d93fe48990d8d4eb942233b6e81f2606c228423d52fa012ba6de7c24ef446f32ba1e1bee5ab78e79f40ddc3f8279277735564901682f03ffbae6176aa8c2dc6871cdea56cb875f25c88d955ad2e5f560fa25acb5d2130327aff86a324aaa721874c4276667404738fffa534f26e4e0dd67a9d0e5d447c2868af3f5b93dad621f528509fa6c3d2b9a391275396d3db6487ef219bf42088433b79d34ce793f024380319f1a0017c0cf821d998871fd35d2da47b774f6c6664e54090d74d087fa43ca622417314ac0a7f2d23da0e9be5242191b9096ab0e200bcee276a787ecb770a60f975c2935f24600869822ce96462d97f34c23eaa50b443a504670923b260e1698eb12a76a094b9a8b9bc2fb07c38b03b495a70e4976d05477ee88b58e2d66729a91f0fc003e56236c5285869a6eba073b36efdac3999ca0b91408282f83b179629bebabd5355381c4435bb173598ce3496dd8616eba46505a64d29a166b51b1033cafccc0c6cce63afe4f2dff7a2cc62a909853b14b40c418d3a180a922b02243637700c87bd0268e3ba09730a2e2e20065a7310d8cb86ae6b2174454ee4c01fc86c0441cbc51f1801a021da1f1fa4ba6cc0e8e35a11a0e55a4aec60d5c98330ea1822991083deb2a80efaf382d29cd0b04fc2b3289e024814281aad94ffde1a837da103a0022b23f223e63b852ce8bb89d50271b29b5dd805edf1f4339bed98ad4e4fa42cd726f3f70c7c434035cd9180a02ac954a1173c2694f0cd59942291c34a8071803a69552d71b8191c4299add494d05e838803e5e9de4e5a2d7b0f8b4a221ce677b5b365e57284f4b74c973c72a565422b2233a5b3770cf28645a18ce1bf7cd0e726566c382d801e655829a7c5e2aa149217075014492b80533bad2c8013c382aae26cf4dc39a6eace929903894f85c8e97e4be1df2bbd53fc780c46ed384e01fb40c515e88bcbeb2005df6c152f0d169a90ce3a43c2c3469a85a456991996f6e9ffab555233598f767b38307fab61336a8d57c1f6753749d46e1b3032532a5ef11082cb30e12bb6b292d0f37806e6208d4bbb7c7cba1b3b410ec1d29dd35af7d38ba79059999992088f06b587bd9570ad0a1e2352a6133a50a3a45da27fcf63cb7c6fa98e6d3d3d7987447fc596edd9a157d72d0bb60441935481c54f9c163b985c708ea1dc510f35f9aa8768e73843ba5e8621912f43383365f534f3ceef986e90664123079f7355e5a5193b0ca1bce0443e01adc00f18f8f07a9038cfbc672494715221c0b8c5dadf7a66875ad67300e5fd662ae7b4281f2df29aa0f5b7f9f70fd3180d7875aaf784eb3da36f9fd8abfcbf2f156392cd196ab952669a531bf941b48b7d2850cb8a66addeeb1447d89daac2079094bd4163d9e0d8ed52e9a93e31a7d4f5d2afc4b7d2c0b988d41c3e247237363dc05dd16d49d159005b42ab9def7290a51beb7811e9f4fb9961a7af09fd4f291f797f8eeb05bf76884ca340af07f62f587dfcbadb344c1b350960686aeecf772f59d3f5c9f6c01240667d1fa5c3d84dae1f0dc9562543e4636d81a3f0407bc29127bf4b4527d0f6d7815a941fb53e6527d5940739bd369786e710f6b0f69b05b2e0c49bfa51411319b9f7b172fc11d901e41ec54887a0fe5748b1d2403d23ed5494b96639126c1d3fde11d85c8c717fe8b01319a9668b109e2d34a6ef5911aeb31c88b71c32a6723b0ad2a28b2d8c75087a546550b0971dde64d3cd69434914d5544a8828c6e7775cc802fd71e467e0327d692cba302f681fdf0c7425ee580c413d1a3904974e55e7ddb19de793541a7a4310e50990744b4655e72b4eb5a3a8c5fcc09a545198c6475dd26a43338753855ae4dbd845d3122b16063b9d4f1170b0fb1eddbe15bf6a0f935565ea46bbbd6c00491a9ea8288822dbfe8788aec3eb3e5e42140fe3cdf16328587b736242c2aa85828dc84352bea3c6d985ac48437bd72f12adc7fc08d03371992547b91a3061c9f9bc6c32a8555df262c14dba813ae8e2f9c732bf7360b1e94498f5a1248ca4a0c020e381d8c25f4924f91bf0c649207b1314bc0088d2f15f662a2594962596ea66c036677c451e77f051cf9c5f6b83ada58109f68b415af541dff5d6cba732a938ef4d425243a3571f475af0a456a7ea5afc7c78fa195780b270ee1f4d69fca7c93087e103595af8757afa5dd23ed17bd723c1edf8701a1b4bcb2102ab2aaef2df7cf485370d310651f6e897cd2d687375d29c7f101aed941cbbbc12ac7ad60c3147e41fffafb766e548f0e96bc911f38a6d1864277c170fae28e4b06fee1d1925923432bbe28e41f8cc19271f68196bf4724278f8492a6d5c0826e03d8dc1232c0efe5c7598b71be7d6c468ccb1077d7c8bd4630bfa4a108d58d5498e40584f6ab690e27ec8152c1c39bb70ccf7c5f2c33e48af63d536d9e7ba90db931783a9cccc05c22dbfd90f84f75d139aa22b1246a15534f1521ecb9031ff0686c39b728fcf6e0323cda56b641561a88c6f78cb1d880d102d26b20213d03af41c91ba6924a0ae17a92fdb375492fa0b514ce2fe88fb95d72e5b1b11bff99d1b0ea0832a0d3472b5880b3ddef0bc0891833adec1f7797f86fc549b22df2a0a59390a900e588cdd2405e5afea987db98554585c85fa2428cababff59c17713cfb100084192cee9f4ee3855163ce76fc59ebd4b23766b0ca7c2d59754ca42ebe3c997cc4cdcfc2ef455d3afd488474b4da2a6ba278c196616f995c4398710c291efccbe36bb724bff2d097095f440f79917229a6fce31dbd89e58696c4571dce1e535adf5e1b0942fb5d59a13f7fb4eb17647e83e587acc50d920db2951bb48e1495256e10a62a8d43d3cb7719c54c24f546ce4937e2ba7b1911f5684068c365398e41f7153cb1e2b9ac014bb7395a36efff14ee94373dd128d725dd6ae6af408a5da12ee540fd8d2264b24cc7fffdb2859ce5227ec3c3d5e0bae49b5f495a39e5d6b3baf7c967b21a55e3528fb0bda0e9f2a3cdc37904005a76b07d044ce439a345f5ab69a3db8c711d478288df2d4776d53ccfbf49a06d0d0332814d34053c98ee3464bbf46309b763114ddf1073100c34466cbf3414b4bc7b8ee73c44d76c46435c0354a71fba7d8ba8b0248f867cee79a4beacb5dee9c45577e5b423582fd261b6164de192e98aaabb47179a6fff8827221a70c77df46d01357bf27392d4dfe3b8e24abe9854d3109b533edab16f495eaae07562dfb4428cefbffd748f144329f7653931a9f65a3b024bf19d2fe04764d53730df662469a19c4bd966dc925b1ac52d9094ff7dd7268697468135000a8766d8292f9079f9bb5601193df637f8192812a9a97f604015ec15c89fee9f700509cd745da588a86c7c756bc84745c593bcb7c7d9d34bea98d505a57b31a90833e3d1e154c3b686d5d8a20375111bfc6964bdf2f815e160754b56c59fec8fa533cca9e21d4f688ed94cdab427f9e179fd308856ab7f7206d13d65cace5f8d65c8b95270559a7d33c76c81794ec38d75e2b3f9ebd1351e07da0540e3981d447558d6c21ce6b0c32bf61a3f554cae9edac7a92e2e25965037830c57f30a021953d91c8349b94181ca8c3bec9f33327caa08e437e542d4cd9742ea16801aae94e19442030e8c95bd4f23d88ae9a1f1cfe4a80b046f2e54885c54572a215f17bf8359db4edc830c48941153bdff79a6777a67a9ca0ee3c5d2e4497871f4797974c1d932fa3417b909ca5b29dfb315bf1590829f1cfad84b9e068213b8a6e9171c1c41757e262d371b7073d1b90fde5bdbafab76426f486581bf52fb2a53df2fdd0a6d5065afa6148606a3dd311b155dc0dcaf9f741a50d105053225f1c223ad0fd8af47d27db808cd32f733fcc8e2655523fd36a906c6e6a1d6eba56311424458b446bc1b77b866af76be210b9a417e87903a1ba2e431c176a82cc5a71a4ddf29350efa6dd8d1707d3884b31b9b52bc876b6599f40b077c43d815d2e02ba043505abe7ecd7dcf73818ceb77a09ea4e582616f4af11133a80429edc9a434f3cd2b41192edad842b433ee7936fa5c7680023f0727f4337c53912fdf6fc7e6d5c0dda2e0a8722d5b0565ddd8f9349815bf38e8615b5c7ae35622d7feb7907e6423c8aa7bdf57355c0353da377977e180b88ecb1faaeee2d7142f342263e636eb99c78af9cd0ef0314d9b57fe1176bf8b13c221b5a05c1f876194ac26c35b0ddd91a2130276ac402314367d99c2e300a33073c06dc18cdf5361908d58a9553268c6d0ba19ef6f6d0bfbf2b1e2f5d284c74695189d8a3236e927d9598a4be253d0492f98015851be3693b0c1b3c4a73a95ea57c20699240887d501db5d3525e787dd1a583c2a831a9b59886c873b8ea5dcec376ff37fdabfa21b74b3968008dfd9784fec35696779217b4f5507ea15852f751af31243dbf857d66cdbfb2e290b52f67312fe5d93bdba25b1b7dfb0f3488974a7ff0e437cea09d0baaba7bbd2506fcd182cf0dabe7c262a823fa1dcfe00bfaf7cb5f54283763a45134dfabe9927fecacd648ef767fdce12d570693ca6aa10c74c41761f64f31139f3aabbabefe6c7bd79a6a3334cb465f4203604c0288c49db25bd25c6c4c113e09fbd65c0f271f4a373294831c33f7b2ef392e4a4b91a3973d38473b9f3a485cdb04b7f75a833e2c0461c6f27e6d8f518d3a4366ea3bf37f43e4408f00c4c72ca8d0a9fdbf1ac7fc9fed47e8a3f3f33ebcbac25fc2e70bd39a85519181383597ccc3503e597a734c98e98a15be8bf4b37330f585ea138e5a2e4104ec1cb6f5a23df7ad83751c32432033f072e8ee3cdeaae4e7de98f9e58633bcdceab69ff60004cfdf51b54e9a33232e1df86ca9708811a69b8759e5b2daf57eaa8f1f74ae6aedfd9106c105a6cd715b66f561c0a0500a501a9f3d50a0b5540ec96d24fa5f6157f5465d413b204a224e2b752a49322a90aa340e4422e60a449cab5b6106135ad34ae309ada87ce9a99cab959f6c2502b2da99d8ef296884f4255fa88f38908e4294f799afbf3d664900c5230ebbec54328167495caa99942c13e834da5c488f2dcbbe4d2448eea2657a2f82a91a53d09b2b5445de8c8c8ede11f028e067620f2ad83be80d19f3b9a9f3b81bc8a405c233b2b17ce8ee5e0ef05847aeb36f219817d735faf6092d13569cbf069696bcbc50cb50cbef921f8fe76775f1034e73bf04431b696eae4943efd78ef6c19436c76ab389825e57b23f4529fb848233833afa418d85a92fc865e634357071bc415da56078f44bfe306d3b00574c04b2e94d6b801d5e3bc03686d1bb27dc2753112b2c22a43610a4dbe1e78c9f4f6f2b4ca3ccbddc2ce0e78fa34cac5935bf0867bf14f40648c805876e61fa841292ef2d2d53ecc00319a6df0bb6347dc865e39d9da574ad93f9cc910a9eb920986d5e6b331cb29b45ff32ddbc9e09b71eef73c8673307d6684a34ce85ff3a4208741b205d31011555b17b7c5effc210ad9309468a007d76983011b88c818eaeb3d3815a02d76f735433192f1bc99a6c7dbb57e107caf9e986248ebcb2e9c28279ea1e19a8f8027bd6f8728c9aed0b974322254e686526cd9ee0c6720de4eac01cd83e5705570c249856ebd7ec76cddd98cc096fb93db8ed86a5aa0736c36d978c09b915f7b9290cc255ea1a4b355888fb96e506cba34683c6a890c77d92fb01bba6b69f91efb3e4313111dafc3a189db063195bb650e0cf353dd975fb5c286f86b8f7d982ca5f74a78fa0fa3d693980be09298bd41c4ef38874ddb95e928494b9e345525b00e4760a1d6a4d54675e39b1d955e20a58f3ee65ffd1e249eb3558ba348a2be114f396c422c57314ce6df0ae9406dc6bd0bf5f1db49f012a03834b3314dec7839860867033643aa7dab3fe17ee025812ad2c05484abd0bdeb67043be2a57533d294aa03f24ec8afae512537343eaa16b07eabbbd2cf6755261ee473dc457ec36775102e1c8150248e4e51dcaa6abb7753920bd5247ccf6dcfed9dde98c9f37938d1c824fce116f39f0d67ab9c1fbc79e034e8bee6f79715433d485edc70e5c098ef7bcf2972171da06cd2f19cde8f824e0f2bbcd816f7eb87b7bdad659a900f36df708d3446d8e412b92142d8414f3a0508d106473e809a0831953e2064c655519a8f22fd0c517df7ac44d72a0e7b6fb697b188133fdea4d16e51c6da3109f7cec1a43fdf47957a35f4651454ae04da9e9d5a1668f46cbc58422c38fdf204f4e9c0df3b1f0ed2e14d0e008bed642353ede25a5c25684e7d593eee9d14b3a5a81429b30895ff5d1f7f4a124d2fc9bc1eda61e8e4bd73b57ef1a168cdfb1ce1d70955567a3d949bab203c379e3676932f96c04ddd0271a6afa0bd55988134de26bf5d6c725c09e04aaa03d393a82805a366e0d98e4b65bf45e05ce45f4dbdc1f8ec052102df08f5852630b706dcde9a3efebca3852e85cbd7fb6e6e00b4bb397004db1602b7f8c0dd365eb2d946d00eacf0ecedae83d26555a1bef8be88eee9de9f9811074151f0cc747f36d45ea7290d53c18fa8ce73f77f54226e5b1719a9bc911c9d7565693836edeaa6cdb7b2e2f9cc8c86b7c207cc14c261793df6c4c623355db04d5eac01c011677d26b764e9048bff8d76e2b6bdf792c57e044eea4307f5a3a62900df3f57ae88577644e2121134d61bbf3100fedf83371438b7854d06340ffc00383a0a4789a6a85062aaad3761dbb3c77e61c5926b0488f711a77735dce429b6e028373dd341fa2db5394867242331168665b4d7fc4ef96b12a571b8b25d44fb0de64b9820734e5647821a038428d67dbe95885526c3f2b8064e8baa5144b7fabbbf5a133c4b8d7a2f950a4fb69f9cdb37ac28cf0421894f0b771f80db988e25fb4ba9991664c93116bab82ba808693d583a2b697fd2b5eb673f954255da410df58231eb7c89b8257c5a68ca0306d088638b2faecbe3cacbbf77725e1685aa81d965f1ef663f1e474eceecbd52943c2e4e0456b52aa0c7758cd2fe67f3132176113a16dc9bd11d6dc03ee643fe8bd6677f2ec0ea754ab499e00ced482d78147ecb2f91d3154e03b57dfad4e4cdab98e134a609a7512a5a0e7c3c4adc9db444238e0fc3c3586b389d8d090ca46222a9533672eabe4fabcf61beaeb59c507f2941ced1ee4cd2afefee5aebeb354ec33cb85c9695426126f016929b471b9cc07dd6ff787fc2d682cc1e80657bd656acfb865dc91d6b032785cdd7a1d66d4d5463e142b6f028e8fb5bd6c621109e7022818b39197f85fd6ad4785efe48c4509e9a537f10d4fc9d23f275fb9e684c2f827868c9ff3baca8faa59903c829bbe1992541d15d538e64329e886ade0c5517cbab3b15fdb4e82506e27f449e268b488de32fd036fcf80f6c97505a5f33e4d3a004772a8970a3fe54049513cbc57131ffca1c82918a81bcb879b8f48d4300b11cd90069912ec9e59d10b6bebbc24ba1061c62a8762156df586a4aa758f13cab5ab59a3b1ef7d038c68920eb0a3c8a673c27b6fb80e5e8741b3b7fedff0c068a83e72373da07c8d73abbd9fa0084bdc92866917b546dbf7a6304f04b18d9bcebe2b4bcf5ac7ec526abb4a52a19094c8c231668d063c5558bdef0436ac418f42c5f41f2df996d19d48f8bec412de05a6a3b079e5b6ac1be0773814acc3d20166874ef64a6cf27f7fc72e0232031ddef1a6a4ac9897a86605550dd635c995626fe985aaabdd937bafe3e2a4082f0fc875ba7ea963a8e5987ad400423967bba1c0e369263ddf16fefc88901be069fc617ba6dca90608cd4a9d86476f8cbb41fc8197a9ee6fb331aae55959d107bef29b4f6f064651964999fb8dc904b8193dc7582fd47b0b0fb912f1c0c3838dd489c374ba5ea614cc80d4d196291c4a54989ae628dff5beb61612353ea0cdfd75962ab057bac887ce7f5df01861c769b88c7e1682f0ec6f7cea63598c3e6f41093d845db95377654fc56702a4110810917521ffada7432b7b16a64699e6a955a4273fe9220976f8b1ae9ea79678a3a74344a44c32454e7bbbae9a1fc440811693f66d63663c84bf847e0a2e8299fb0a6413194231a666a2efe5ae1c41db1cf12dbd8d58408cbeb1a9507b5b09ebf416f712b0040f8300dc5188e673c5ab1f13156718031f25cd5156beb8e77e95b48f42d1361da25a94cf1bb4a2a5c41f8da5c3f6a30c00e7d0323f7bc14583fe20815f468928fb69a480673b6c775b2d1af6a176abe5f6c679aae41ee1d5c89439a798823b65ef718515ceed5a7ec87eff54479a9afb48d0fcdb9a240e946719598512220d2afb20840e3680689cbbbf096e7d7505d1d854faacf024935a672278c5dfad333373015b11571b2a7329a0d2fa792d838ba3fcbe5303f84cc6d0454b89cf740bb0f83b79c98b38fa17dca42c5d57d8e86cbe16d46c4c0caad1f55ac4a399d9755067b642299b6b023e47ef74343b9d856545a51907f86034397dc1cdeb0856ea9fa753ba82c5f7e44e4423684f1b03ec59e5cdd96c03dbb1421e89bb0d1dd570309d92519e2b90fc83ece91ec6b13134cad1272f0fb1092a21e0066cc0a783e566167b52856fa6ef12aca963dde340e94f04980279f67a1eaec339d0656c09a3fe91a2b1885f1c09e831471ebe78f2af2efa1fe5821034ae172082de7cf5fd8599f7c575311d35248f2b0e6342cf7ac4bbcd19942a74ae9a9ea3ef5641f70ad1746406ad9bc24a1a3ed17dfda75ad54fb920ee9c92b792b76d42cec2c22fd1bb0f7acfa398d82b5f016f2943dfb44ca1d96b2a54ceb7595837379d62feac76dfa610a82ec947047176aa74fa8d58aef3d34e79768ba7b213a8c44c6e9739f05eb197148b2d2f3c5a1dbf0d4ddc4ae2bf0eeb93d2d4d0990c2c21ae34300a3139ccb5cf216b4031f5be4fc61b137a952fd516e45236dc791b692910a09a600fce9c14ce9e429ef1ae3ae737ebe9921d10522a4507d0794fde66f639189cca95923c51f97cfde4d57c718b6e0ee709227fef69a95a229ab54a4ba9d858115ac04dd0a594ee8fba7d29a23d8df53c131dbd6f7f47c6ec482cb2da38e98a8bb51a2878950877408f0892199ae4e94c4c8eeb614eeac1435b2947ba67c30ff52ff4c958eefd318814a17f53c8ae691d3fd91ea8196bd82d4af9fa216e32636d6535d5c68f579d3066654f2cc067873fea5d56f9676800892f210363fc790086a778fb721854a0abf9a2403ac6de964c2eee9011a44cc0d66ced8bb0634cb986a12b0d5d875db0f26f008555f7ff57f42a441250dc5d13dc5342ef36c288127d46b4a2cd85e5189877003589579aa787c46843e47666be2ca6e8aa84e065ec77f0f77e5cee75785e2358a5e1fd7d540ba1d36052964d7a5de9d1598cb71b238c6a6f2a4b6c72d8c5b0a4c2f17342747dcfb2bf7bc52256fa21741e1f8e7c2b749005ef240296ff9935f75e943c2e725e9f54636c3466450a49eadcc6f330558f2146426fe6786f4ac6fed1943d616474f7665e8ecc2612479eac464c8313066de5fb652beb42ee17c0d9eac4496b5a732c3df31d90e24ffa8eacb12b72dc1d393d0880959e0ce9db8141ae1490bb12af9bb2d12da5809a348714024d7beb73dbdeb4d7adcb68c59ada159fd1855ddfaa33e0b985d7bc3f509e64ee52b5057656295dc669939c45d89a07f03c0062e20e309a0388725c993f74cfbb8db38b85f13bae8b27a4da755398f228d79eba2ad5101d80c75eebee608dae268dac0c3a1c63e311fd82cddb78f06fd576314ffbee8ed598818894fffa54b56e7a7be5c20ac553d106609f4bc757acfe7b116f695fa4ac3ac93bd0de4bf87aa6aca5535e467298aa934c11143bbf39c464db663b97ad416b6501daa12fa8d9aaa31334851f36f654356467cf4f42fcf0c4cb239c2acf7c7938c0760a6a8f953e75471881c59293285241f7b73e4b3dd5" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n14-ecdsa-wrong-key.json b/consensus/common/src/test/resources/aere/registry-v3/n14-ecdsa-wrong-key.json new file mode 100644 index 0000000000..1c69cd6382 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n14-ecdsa-wrong-key.json @@ -0,0 +1,62 @@ +{ + "description" : "row 1 claim signed by another validator's key", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-14", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0x8029e6ba97e0cfbd9ab4ee6578e3ef471375d5e3e592955eb6b947715ab035a31e58951dafe664e6de6e5fc6aae761a26890e81114e2c4429c5da4221c0e283701" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n14b-ecdsa-missing.json b/consensus/common/src/test/resources/aere/registry-v3/n14b-ecdsa-missing.json new file mode 100644 index 0000000000..360ef92ee2 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n14b-ecdsa-missing.json @@ -0,0 +1,58 @@ +{ + "description" : "row 1 has no ecdsa link in the switch epoch", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-14", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n15-transport.json b/consensus/common/src/test/resources/aere/registry-v3/n15-transport.json new file mode 100644 index 0000000000..77a8a6b43b --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n15-transport.json @@ -0,0 +1,62 @@ +{ + "description" : "row 0 transport is 19 bytes", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-15", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0x11111111111111111111111111111111111111" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/n16-declared-hash.json b/consensus/common/src/test/resources/aere/registry-v3/n16-declared-hash.json new file mode 100644 index 0000000000..2fbca8a3c3 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/n16-declared-hash.json @@ -0,0 +1,63 @@ +{ + "description" : "the file declares a registry hash it does not have", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "AERE-PQC-REG3-16", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + }, + "registryHash" : "0x0000000000000000000000000000000000000000000000000000000000000000" + } +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-a.json b/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-a.json new file mode 100644 index 0000000000..f92d845f50 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-a.json @@ -0,0 +1,71 @@ +{ + "description" : "epoch A: 3 rows, falcon-512 + slh-dsa-sha2-128s, the switch epoch", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : true, + "previous" : null, + "expect" : "ok", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 1000, + "count" : 3, + "0" : { + "id" : "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", + "keys" : { + "falcon-512" : "0x09501c96d2f5cf0b28fa00c726e5359c5d36a1fa069971f854b7d914fb2f1295e68b2c6fa91d82f7d287f55ad493c87320bf7ca2a5d5e79d7212e8a540c166b588dc03f73b83be060525b367d9f40f5e77e7798c60b6499767586770722820b3b9e6013cad3eab5fe5de96089a29f34919a2e9ebcb3fa03a3e714593b7a3356d11ffb3441ab40ad94474da662a71aa2419dcd50668cd5d8a9017662cb60998b34ab5b7929c58a94d5e133ebbdadb2b0279543067f76bcc4cb6d7e0a5edb7625467e3ddc92a01206e1b8dce69be31928687d8af4d26633a07e73c2830d5299b878977691cf65700b65b6ea2463e51b6136888c544545e59fe8cf190a7944e215a785b682623c95a79eda28e25e0e1b91b49ac89deaec3b14a3c431d9046a663170111ee541d81b43aa91f29dc147c6fc97739b3a63fad1ca84843d0a65b8d9396f28e9b11f9a41453ab18a2309ae8db03253a09fac8f0cd634c76a5e938a44719660944a95dff056cee43ea99592c3899c2880d7181ee3dd5c04baeb60e1369a69430a66031e3cb10e392622f5246a71761760cf417dcb40ad65f60704fb74ad26f26c3af7513a42f806895e0298c25c049187aa536e0eba21998912e621846c2ca0e323744ba930a29850fdb385c8241382296ba28dd4df05ddb1e61fe525949034b59929a784406de240f8086b4074825f419d5472a28a0b157500be37e9f64897db2b645d3807c48dc143266c99e7cc1638ae278772493a2a4ecf74e526a514bc9119d7129c5a0d115a450ccf4dce05b76d4b6149fdab028006df7ec2209923117abf7578a082128cad42aa66b69eb63acee4456bebeb537ca57bb3d3487f95e4f06c5cb29f292d3b6284e5a162cb83151cd9ae56aebbfda150b7edd5065302ccab70892733a1e096ba0da5f51b42764ad23060eb197d5460425fd9c7a26e7dc3cfd40eac3643539feea72c9ed41569111a7976d2c345ac2183fa2271b424526709fd9aae558a23a5cca29f4e2f31629746a71d904612e95ad9977bbe74fa32e90fac62625838cac49f50e5aed4e8879e2ce2e5651c4f4ba0c96583af9e1612b5ff1a6fac5027c0a308815e592308170b915b4a2de1e8d48e9a5730680ea9db8f3326c430241153b3b218d5347d5c843570cf80ee813871ae10f6b8ce4ab7e4c5197045e85b1cb4977f44c33eb85894b6555b4bd288a50c1b656936841048983c00a01631f10a84522cd613fed0d4beaa4aa4b3e9f30c324108d909e59a80150", + "slh-dsa-sha2-128s" : "0x47a0b70170dda85f843607f6c6fcbe6e6acc7c22980c108996a3fe72dc9f61a6" + }, + "pop" : { + "falcon-512" : "0x39c8813cd3b34be58eaab900555a67486034b1923e0b00e435661230cab85f07690a5958a5f5e9c84f033a3dc2039a5296443093fb7fe8d41895fa73be3fb34cefc6cc36750d534e6a3c47307294694729d6cdedb633e8efe8979a9aee486eaa66136e342c578761241192610185feb2d3c196bea0e79222437b6a8e08cb09778d053b29552df96507b1cdaede8e9aa7abc18902f29937f30e7cee6868f349f7ae33738237dcaf86b8e8e01238f3d8ea6fd4090a8161db65dabeb5ded5499e8cc8a5b32607abc291b36a4b34b327d30cdef18c1825232e69a2b09d539ed84dd2a4756efa64f864c563943b3db825824f294c1bbfa2157347b0fb0e7f8a56ec253b39ca9b6cb1ef7d3d7c93a6cb220a7dc50c365905ea5a71575c3aeab25b33523e4fefbaf66137f45c3d96db719a12d2e2d42fbd6cdda58c5a1b958b28c2a8f2cca297871a147dda92a7f008a359027a9288e42ebcd6ea9542f10b66dbbce6e7bb914206122f866d9adc1c89cbf8bbad9bc9517c2ea9018b61d4d2df9abff1dd9f3a0a6faa72a449af9c97e8c4090f3bbd9824298e61599c13c7eb89677c0e845ca2907c728d9296c24e96b6a348dbea7cac62671c9a78a531463614ce321e28ca2f093094ba5f69881e6cd766874622ca769ea556ae757cb7f9546d99f5c90c25e7b8e14f3b7f270861398800b0604ec2a3374659277a23cf6131c63148a41b05b3c06e2a98a268f1b9f0df7969addef17179ca2261d3c229ae3db0c490834564c45f7b8ff6d3bf70784779d75915fb2e2d5bc147bc941b9e249af188f63f39b028547cf080b7674b228e20a600e3b167610f814daf105226f3c1ce5e07834fd33dfe46315546e926fd5be83fd4d47ccd5bb8ae736ac745daabb59c87b9edf9a45c9a1d1617ee84e588f27d12235c59ffd9", + "slh-dsa-sha2-128s" : "0x0e8f9bdc22cad3613ed07fba30ed04219b9528f9b657119e2f0886dc71cda4afa7d462fe8bb108feb58d998b370b2f7e162390713d1a17eb7de781a604041cbed13d00effd3b24f2ff87cac74f61e7b7f7aa6a259dc283485179ab4bb966793be018c4bbf4475bf7a69e600a37246908f662813813d4b0f4f83757f4064c5c0713870a0b98b5bb99025dace06100497fda8c73d7385d123afd4953961066e7c8544de3fb620ef5e23174327c2b64089b3fe6df3eb86e83b12634a0c9cc0d7844c894ba6d83afed6becb6f5abcfff397c5cea8064e5e153ea4253abe599dec1614b449329ebb25861b69a9b22228d1a03ba51532ea64ebe7603474a15a2806d3dafa29d57d27c5fa894da673b85b4674ce835fcfbd391d328ef83504101432bbb0c83d7be462b265911aad862118af6f4cea4df0f1dd38969c69428fe438d59d22736fbc7dd5faf6758905b26ec4c99d71e4b8fcc562a694ead91569c9a916df5ad693cb2ad0b7ae4c4f3c5b7322f56612c5449e0339dbd367460c76ab89b6f44942cd33f35eaf33c2ddc0c6af793556d5202fd1fb0f9fa1b7ed45c6ca68d904e476477b71a0bc6ebe248c9b4585da0c7bb186600691269bb4c836667a6d73a455b97127d898b7fae17e1acc6dcc3bac3ecb14da131d5f8adaa64b054e2f569d9bdb164792038e107848882de9bbeff295905c2af68d4c6dcc8cf718c2de872ab6b3d18e00d527166f8ced642281302e2acf4cf4489072a2dc3d55ff8716619cde494be1763dce925438b33ff1b91b35eb511645832720ed92552d4880ae53ff6516cfaeb1d48c923648e502e4038676f7c82e532ea713c6b9348c80ce3c5735f437491d3a8b1765294868c14123af2e9ff61e394216e630c71ff976072d2a1c50cfd8ee1ce27aba5bcf9aaed4a51521bad5e5d5e66abc7a95b215fc8004364aa4df2b8b97b14e3950f521b6327a55fdff9139fb0bd35237b022e3aa128d36c8a60f4621d25d28c59a97418c7dbfc15d900ba626d283632732631ec4a948f66dd9db3bee8c2cf60dfdacbb5ad4c0b2b31f8428868cca035e79c95bcacaba40a9351b2b44cb29753b6b9da0a751bd5af84c9094d5fe91e97501d4f075392290e8516ed5e41b41b00e09793353707fe5c1d942395074637f3ee0a0f105876d6f28a74f9558c5f360e98f047a99af35ccedef8d5cd299365e81e7e91f28a01eb265219e21aa7f7ce56b62f9d2a61560f9fa39f2765f5b50920658100a638aad03482a687905cd27dca02f1deffd1bc49a1be1ea7ff1a505da74fd932937d8f6a8bd75ef456d5f7b71ca5357bc66db6cf14368c4faddf4996363251e6a6113ec64d642c319dd85830d8b4c7ee1437ebfefe01f27709ba0c412c266530f5b6bbb005994774e8935f1d91069f9e7a8cd2bb7ad6527768473affe24709c10294eb588aafca85b379bedf0b142b109e1b186e635b0625c4e59576266acb5f71040c44035fc9480b13932bc657cf332b12da1cd7d5c3977f51d5fa742974cd256e8e3662dc698e0516d615d0a05ea9caf12338d0dcb6151e07f184c9cbd5e59bc40b12ed6372e2d6bd42aa0eb6c3c888f264a8c0d3332631d4919c921cdada6bffbd01ca5445de0779f9723577ebd7e33ab0e5f577843aaa6cb8cb5f0508c33b63913a44d02b76bbb1eabae325581738fbddb5d4fdef8dcb8597bd29c6ecc631839118c44f66ef8dedeca65e58941c30e0423c40bf064b8d19d28dab3787238781d492720236a7ae0695ccc867fbb1cf207ebecc64f84daea66b6745515b9ac9b725061bb9958ed79ad6161318a262528004401633d47a023c645683e36413b8653fa4864fe98c501197c10226b1732337842a1322bb788a00a0de86758139c2d9ea3c5b26bf5d7a4a92cab4cb686c580e3e6cca488136f0da9279073b3dbef9519168ba02588fa29cc60d02273805b425b919a7e2e89c9086609982163e09d5f6eb9c66267ac7be363aadd4617d398d4bdeca37a838d650b0919faf13b78642a058b1bb96b2ed1482cef9bc6e8b2e061e6815936e15a9c754353aaa786ab141e95e5f6c464a41b8bf29e28e764abd638abcfb342b54d08c15ba7e24e6fcb69b844e16cdb5cf4f50db60ff0699f969d8e68511fcc9e16a58e100f80671af6b04ff3edda3a4f05f9183c0c3bb7c1061fee2d3f04e33fe75f1354e6de54f582da914da0138e8565ff45ef52f638cbc33a24bae863fe91e58d7c5da34273438f15e10d4f59db16e505620d97d3fa4862a4097a659925d0e5f4e073103ed29a600b051b83b1f3a21381f4b871c18d262d7e43842c34ed80661438c064aa2fd858a8f3bcb035b5e942baace7b50e4de5a73d5c51fc0843cdc163958a62d333078f4c7d883bd7055a602e04c853f9215c9d7f798403c007fcee124cf6099322629c449ad4ef84b7356110798dd87aa4d98ebb47abc6cc8748dd381d588b77db7f4e7550f6ee1ef4360dac6d6c7037a65297d4a0ea7999a5632b90ad48fba0a916846a216941fbd13d62ab36e1066e24990c19592944078435c52d7717b3556595b645cd2359ad151dbb67e92b14168cac12fb2cc5422582a50bf6e99f116751171c9b78744ef719282f1b84bf95b4092514f4c68fb82a1526e0942ca50e186632cd9b0b8b8d91e84f85e35f30f815601798fcae676da6a2d38eab62f22445099694804a36a34142150fb795afb445e78c241f0b645209037a8871cb5487c975975ac997a540b709e3df6bf208d9d8017f9688b152c9ee2b934f65393de8403244b7437108e6d8c973aa723285555d9eb92509b1747977105cbac7fc33e658fd51cd374d21fc2bf229928948292c1698db7934d0f9be44b137514d0a5fbb659bba83861931e03698a61cd20a449d0358a5fce7f288e18bb3eb62b7ba0ae75feeec634fc90fc45ce45239c5e3179309f0c202cfc53d9f42a617f7aaae586e7161a0723ccebb7e8586cd40e1fa12d3ee89b400dba8073b0080ffaab1182e8243afd93318df15bbf3ce179f21804fbb8882e855ef9e0739ce1536d8ebfe9856aefa9874b4e102e705a40ca4c3ab0ab7df63ba525189b518796590035ca780d348de49f0ba8f28456ca6f3e3cff7e761d961ad98b8cf01e855797c88fe2f811e41e787f3e4becb0ef42ca3b9979356020b447294ef295543e0ef512a247e4d356bfe3deacca8ddc5255e6ad047121eaeafe61d71e7f31395cf78890add4f8c688e4eb065479a63ccb3d0c09028bf60c700fe9d19195c26fcbd4130e6feceb52e192a2aa2343c05b43b276f51a7c55dff7233ea512942291900d93315438a96af08e25ea5b9ce656c54e71ed60d1eeb1665ddaa9e42378c930e21d93484b9a4099b7ddd71d983b4c42c6b1f62907ce84b24321422b08b847f1ea97befaa4ec30a794ad7f340210c800cf185ee2c6c72e844cc5671964e1e1ec58953500f149f0f0f25c34d5592de421c2e31535040020237b907e86d753094dd4532e1ab9d821e7583d4a21e1041fa7ad711f29ee3721a2aec6b0c46866a2de71ee850d8122f722898e533848e2bb851ed24cff9ca83badcfad88a171b4be064007aef4d759d93ae9b67b446f3c6fe504f5b4fe851b8312fec86b46ec3cfcb36e0f682e65e117702b888ab8566c9cc716a3a86106d2323c0f7f20fbc1fcd110bed4492a750befbfcbbe16820077dee281bcdf14509b93137a3998b2fdabc2a375b38857bed12bb8a9c67bcae7934d02d6ff89620fa8946c8aa524fde99c12fec0e25bd79788e2c15fc31fa3f27502cb952b9f9482580cf18ecabd92c55113ec4c4470c4dc06d8ca27a10474594eff708209e8f376b6f17915c2966dbeb0e35ac2f02419692c0d232e133b3907059d3a56ff169e7e61915c8b1067e183337b40bc1136241e862eb3ab3f2c6a72b18511c4107ed063d1e2303d0fe6842fb01c204d1bffad7e90bb44a365e62945a7f631811c9c37ffa5ac39c16b4835798686da5baa8e1eb6aa76b42f6b86863c30669486a175d6b3bc5bc47c5eb05b240bd921007fa39506c6605bfbefc9c8c493973bf3f0767817fd8ce126a301c5b8fba79628a9b3a03cad0c491a5f487dc5e89269e5d3bcdc2998a136a012486c5bce2e6c6dc595aeee2ebd5a36dd06919396184aafb2301d1889b668c4d701c7ca01b95f9508f47d4c411cc0397c244ed4e3e3ad7f97d86b6f8834b13b1b2df0625e6b4eae9e439815e65baf2da767d3b1700fc461fad6297fe4e8ed02bfb65b750c2d2801b7308aba9657df3c3ed7d548687f55e85c22343d1db343bc2dbe222fb76fbb3cf490cee7a99ab3a337c626a148fb303b2f8651651588dd4859fd65c95820ee2af85adf4e8fc2c03828ea7427e7c6b0d4a1b0ccd424df6d1450ee436c01351d8b6bffd4ba072756315968845ab7976813d93d0f0f2be1c00faab3ccafa71cd26c5837e87292870cf768c7837e18ddc3c513820c660870b339fc42c5211d33d892ef22b475cea3be20f5e47206efbfdbeac208e26e97c400edfd41f063aaa6b85a0cf86e23fb11274b405f4f796eb10cb97e70783909ad492ad9207ae7b3d204e59d69c886f91bc82a107d0be3ae85797a7cba747b8e2640ec49384f53c785a84cf8de37dd4048dd4b879c58768bd131ad7226506113c446fbf13d769794f1535d58e9c52e23c1bd55e13c647d304e297d968c759a378f1d594897a026e3271aa6b4daabe174d6a35665b35193379f0ee465df7827299c18e1012ac69913a0a5bb5142e99b051763de3e98d5af8b62133e25b32f4f12b787bef7e21dec42de8e4d99a0adc8f365d8cd3e6a5ff3064ca18965ffdc6b383f7ad08186d735a099bb0ee14506388c1a37f7a766d286d748d404457226b952ca6cb3f317b0f4399006b5b8c6eb89f6b8cfa023cec4d2dedde3e31b9a633c0888989b44ab182a5aba30ba78640f477ef1202f6496fafc2a18e283b2e4e8bc60415a1c9a7fd68f15cc1ea0299e3b26f5755ad9f13481a0563a524c6735942f5069dc1d6a070b3c3698e9c503f67a11efe59413e937062668d3439a5dbef197d24ebdc79944b17c549b0648af9b1349cad83c2a0a9e55fd893cffe2aff4626662ebc5014e70baaf7cdbbdfd3f92fcc5eed863fa8afd87f88873f726a0a2eafe41fa81edd5833f5520cf93b0fbf0660e48eb0258d313bafb87dc9b0d0d9349f22fa2d960238f67457ad644bdc60fb5eafd1c88dd2a74db4da0f723a03df08af6c64883f275395020341d9dd3c7fa049ab2bebb4d8af2bb8dc1340d1a80088c4b2c26bfe7314926ee64bf239306c05dc2ec6e6c5e0ed4f6dbe51f5ee09533f558d29ca6fd27092c20dd892c89c83223752006b23e5abecfdc733aaa59c8549e89e5ab0eae1b44189f58cf9cc18de0c2a2f46a9235fbe42c641a890039cf6aad820f1f844edc24e69bc3bd0c22cfad3804d876edf5491e88cf891c347ed5f24464f2df439bb30742e1f9a4a7eabd9346c6a5f93af22e67becc494cadac82cf9cb693acf44560672cb9227eea2575f90b0e28431684ae12ff2e86fded0be3a099f9ddd32a7285531de8963c9a03355f1d9cef88d7ed9b842d0066ccacdb698d42ff9f8a4f000b9764a9aab1f3146f9f95e6636fdfc9ef91c4d060a54ed7f32167e7ac50590dae3a2904d5d2eeaeff2930bd4cad7166fc17edd2c30e6738157eeb0b441491e5dfdc17a7fbb2bdc6426b2cfb26cf2a7e20013f1b3e33a6d6821928443cc7893f7e4a5268a8b94a6ca6097d25be17b214ea4301e9a723194983bbce286f4363a53afd9e94dda9c761481d9d20c7687a2ff85d42436b9d557d5dc6c5e24ea21d5231487df9fc7986100664edbbc51ad4921c5ec9fbb407ab33ef5c3b3bc81f113e68f38d73f0db0261b656fc344d2d23b93446845d69cee1704834d98737848d405f52f5a7bda3dbdfcd3af3881484579d2d2f3ef976cb35c9cbb76252f2e00019363273489b39943d212ccecc647413c56de69e0087532223cf9b28ce3f77fed30bc3ae6a0842b546e0e1a8b6f6ee1ca0ef1b78c3ed829af817099426f94a447f67229cbca3c07b7a8ce547c5eebcf168248e0a0b9fbeb398e5ed2d0dcacb441f95f9e30c108c725507e459d34c1ec01b67065e5e202d685502d6ff015d46c20bbeef1db38c29df7df585b95d79f5687474e71fff162c0ec0c96c94c507f8a0292a8c32168dacd2fff78b5c7dc145e10fee1b25c478172af8f3ea132fdcad75dc22856deb2576a5ff3e8911842c811d1d57ec203c77b82da3658cbfee18d140ac341d38a30f1af298cf60f2a88d0dfbd3e079ae64a07abb4947cc042925fd5c64bb5d4467589f8c495b0097073c081517bc4504dcd7442bad7e7acafc894fcf34f535979364cf5e908a84e0e141ae47e2ae4dc95462b4f345b12b0629502c7bd65579ec66b2d0a951302ba6da1d1176db6aa0c0ea1fa88195dd2248990fa4aba88490b1da4ea405649203f6ff6f734693f40deabdad59ac62e3296cdcbd74061474dd7b63c46bcfe6ea2b0ddb0f7e77fcff3599892c82425b05ae7aa1a2ac1dfddfc67e11921ff4f2c8f6297881dd5e38ccc4076cac8015c59cdd6023ffbd62b887b33ef8d917446a8941d9563acb66a6226fa4d3f2cc21604c080b3f83e73c093d8f2f7067c392671020c7a7eb6fd0d97af0d63032a3116c408b708d4b2f711c1134b88a91aa359608550f0049af2bb97688afc4352a45c4f6d30d4634e70e82515ff1d5f77851ee857e42b486d5e1f3e0406227dded6c5f47eda30d94f44d978322891dc729eb6ae95d29d22c8a5d649193d5d2aaba782fa16ae488f645a7ed90a566e182f97bc62049724f4cb5fc71f6bc7be57f07a7afb9933f6706cc83483cd7dc4182768cd35bc42f1e9b54d5454d3a17e734475ff58fc66d75419d3d122eff05f7b0548188682f1318ec32c5bbea87c8624cc1e548bf18a6b102e7a43e6bafb579e4e84275fe07d8c63ace449a44cace5d9305ff756cbf40e5a6a20334d5b4a74ab0483e31593f2277e90341fa8036496c2bc6d2b7b10b0b354c86d666d65cacc593d1d7349e71b0360d823453011ee16b6280f5c048111b99b185af99e9d901d4eaffd8a96b1ff394b66208ddc1e7e53420fa965bfc4e630d73ad97b9c5c6ea884c619bc24ac3f46234e7d8d35a12b0cad7220d7a0945cdc64fee9d8852bff872c43f359e7aadf8fa3de3551fb716704af68a18f79d03b3c7d90cf21eb4bbdcbb4004f8ba15ff0bbd2d0918ee78e9bde980e4f3cc13558e6fdb40ca71ad2c10f5e5587784bacc6b925f72da12528216895e0ca4b350d078fdf534fe01bc86b45e0c8e2b35799d268a457f8768ed782dfb56abecfd2b9db00cd10e189c6164f82f5ccab423277e3465ec72d76b696cafc1cfa9a7fe47d142fd6672d9c5b77751b40801af07fc53b1c9da0fe6981ea75e066572f7272baef8fc54bfa7cd71cd0fbc5d6cd717e9aa07808496ad2b4b4005a320c9312c73385afd61193581dbae99df6c25f1b9da17093ccfb108b0a26c5ae4963183533ce2f0ade01bf5e818af613416433578522b1b1e5aa7c791570c8c8b26ded266399116f5789a8233fee128e54ca8ce63fd56c1a146c3006316d200e05715993d1d97907ca83d7fd42ef7e7222eebd74f9c86b6cefc82e614a53976272c8801bdcfd882e34d86b39142ac05088a4c47010a71107bc72a380bb3e5122981d1795256beec20a0fe568002d79a4b1577903fe8682f268eeeed0a97861337ffb6861bf378eba1e0957111c7f9fcfeb55a95238005b40e13ce40efe846ddb86d14c9ad2c46cca74baa364ed08da3a3079fae3a36346ac949e2174c661b8801a051423e95dcab6a0f9c965e8744bdd0e629bd3c1a1241e99951ddde184eb8f354209f24c04fcf10fa8f4e43cb55c922c28be041213ddcfad65c86e1ab9b615f91d5fb11e4cdec5b7e25f8e9811c82c2ed457d012e4967b555f228baf4f1648854c624e0525e36e5f4d1961bfbbc3621d79bd6bea72924d8e0146d0965c239a768b417ea4c33237a60fff2760a9dbfb1113a37b01b598f3e06987d99518bd9f1ec96cd7050863898c8664f793ef29114bbeeb9afab8ed09358d1227d7de2aaac0ab2391cb2013028ca5f4d1de6425ec5feb17d95df358572fa27fcd51d14c363a36bc37c7497a0f964a6c004561f68349f828d30d4633e87bdce6b6d92384c9c1c540c8295ea621e9a8006a8d7c163b988853071e06c78f1b9a0246db052e8484a65cbdced3a587e45d9e2b49a5501590cf768d08b6a043971110e9c9dc96abe855abbb7aee2c38bbb77e5c68d61f01f2376b6122450117240532c7c840e3739395dd6c9e80a432423fc5e3a6dce9dcd58eb418e16d7aa507b4d67dd6fe2af4042a5c7398ba4f87c650926084efbfdc9430a4bc2be544880ecdf7361aea06a25e4f592104b0d74312f2729a4db5876b675bc0fa68f6e41e014b7caaf6f49b8ef704b6243df410dce94ad2c51d328fb90b9cf2f5df645015f489a262f57a5aceba40a56d343403b660928cd7a9d165f4a5bc289c0d28f33dac85c9c2a5c79b5ddd22c744a0388a5daf4bab84d7c77685053c912c9a24b5a0664c6253febbcf552c4ff38bbaf2f7e0c9f06e69a91cff9138c2745b99367e00ff6b6a920b1812e17ba44175c033df0e58d1b5c0b7d146f861b62e812ecbe99d638219e0dba4dd88089005977bcc90634c4dfa95f87370d905d32f613358fba2efcface0b9a414ce33b09e271a647b911843933c5d1008fb0717ab59739eb8eb844a4b34bb1a8f635c5895afa285ddc43720e78dc81eee7db822896bd9bd6ae3e7fe2988ba560d58f02592870647b94aad1dbb0d0cb60e28135ceb72e0a6f4b7893a56eae83f9d9a4c549d53d17c6a00f5d6c266d23e7a1d13e22a5fede3a7082dffb34296f8fe9f3bfe525ee1da81efde3aef9b6de8457a10fd4340809e1dafbfb9e2719c327a3c3a4c51d4d5600e8a01ebd5c537a4cd7c4519e963de06de6c83a9b968b01f79e08b5415c5a9d6a92a633d6ae969b80cbb2cf4d6d1ab2c3b94e7c12df27ea89bff4014d92add41a3e961006a194ae3ea4a6b38c2827f6185ae636f6846b1c8401289696fb10c765fa93e58d068bdcd8c9dc95df572f096d65f5de42bae639d41ce7e1ee0f48ba4faf856c0cdd728956d7e4babd6d9d6389ff41d76c5fbd6380f351b05a37022dbce0a6a47ab7168c82c77a2810c80b1882f825ebf86cb7f6d0f54025fddc9b9120802514c797d1d3968631dae95297a857890ff1dee408909223d012925dc08b7d1119fefca7466392a019d4121ea76c8b4c3bd103a820557e4b2283e262af32f1edbffaa253c47d5873bfd0219b8be86c7e64ada2d87ab6b7f428a7d8fb3ae09b05aee71054355eb83ad76c05d836a9f1a65511b1553927ef1b19ab1576d7185af61be6104de1df218918179c6321846085a83bd1217aa63d0b03996ac1fb4018a8ab54fcf635f7d5a8ade95894f0d407e5d4691920f21094c6f632f1661402a0b3351f168f16a13d3a31ad48e89230cffad272926487f36c7a2584c9387623df6227be8c96088db4a21c93b1c88ee756e960f60e614b30bb6ff757c20f3674a7ae7f6ff6a9115a0a6d444513ccc0be5dcd557430e0ee8db3ed5b3e8c405110500b9bf7f6eb7748de4988a1db94bcb16fe33479f64762c53a1e2965363452063d7bc14386ca7b4d46ebcf08489c64ab413ec0f1ddee8719f5fe978d38438ca3059961ecc8480908cfaf139d88db2facb5e7a88a43a67dcb0f97a17f9bd5b09b8369803ccc7ab6d41238e59e43762965814c10c36fa4b6e61bf2e83c0243f20ee8aab62dfa9827aec06f99c5c565c1bc75291292d3113503a202851c26d1a8bde54bb30f0301e555374b71e278925a2afb29b0209c08ecb89601bf612ce811a493abe2bff78e7fd02ee13c5db6b3edce7c87559410b9be3be95465468e33af224d6508a44728cdffde4cb14fb96a0d2ab9c7f597541f9963775c32ac613af9beab89e0ff7a5e35613751061b0511532b10b3947802901505c8e33bae67ce0e28da760af7e80b730811bdf17abfce39d35def51d73a084119449038c2b0f1c86d844123c6e71f28b7038053e279dd11c088dfa11c547a4ce13b19453e88e5b74962101412490cfb68e21d8952db509cd3a814387b67339fd04ddd2163177589146409934f0eceb10db2d90a9e3f6691eb75e84d5e66bc3228baeadcba200f3d11e0cae090ffbb800f5da7032c35ab572de55beea37258bee6f191addf01739a26efaa612991cb2a3c6d1bbb03de0ee1fcf9b7f6c51e452b21c0819a9aad93162eb2dcc189bf355ecd2c3e504713dab24bfe3ed86a4194611029f7f6216cd469fe1f7d35d56b6c8c2045011e238f9cbc0a19b8914d74329e2472f15d0b7d60511814ad2736d6be168aac01868c2a675549c849fe05e05a76f077130b46896735db5490277b76bba43545de6b8ecb53fd20398eb94dd884951fb48d5690ce4bcd0b836615cd9340ff0cdee1122743accfef343811a71c7d14d1369fa49ce47784a6ff8adc26c151edd4969dba513ade6a9b1f009e57c3d6aa845ddee275169ce58e1f04b564ffb6d0eec75f7172e5e8fb814cd702c9fbea8a783bff637db8667d4b6b48a264135bf60acf335936105abacff0351ba9aeac88e9522cf7ed80069822a7db1a5d8221d9492e02ca893e1f15965fb82a80ee2ec491027bbc8906e71cf931fc21e1c0865ee06a79c023929625b4d232c7b3e2e4a4e5dde28fab675391befe583c95dadae71dbaa43e09d8d65ba36d148aa85d1a2a35dc51719ac0311ea72c1237c78a8b1542c59de3b743087245e54d3111c585b5e5e7642f625c8d44c6d9cff50d8d071f6fa573df937d9e7457302d8b293d8fd94ebd3023c55f8b260c3454e93f58f16d0f451189bf41c9f9b91d3c3918adbf98e4ca5aa0eb6314041521ef880ce18f0ce296721a06b329bb5fbb3024e424b46400e03caf0105877465285fcb41a93844fb90b7fc1924883ace6ca8a37c9905068c633761163affae39d0ba55b67fdc6bcf0908ee3a42af286c" + }, + "transport" : { + "secp256k1Address" : "0xe23c5bba38d809fcb2d3384f210e9a6405beb498" + }, + "ecdsa" : { + "addr" : "0x48fcd978ac4d42ff52dd81fcd17e93190f35e5e0", + "claim" : "0x2dee91bd2cbfa906aefa3a8ae6e2de773f55dbd28e77bc329d28215183068585601ab9a7f9ff0c48095407474d502a3af3f059a666e5ed1e27468466d69914e801" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a6d094d9a3a0865864e9a16b477236f1ca26c888fd0c918db331c87a478519c41085cdac7e6cd0d89190a41a3bd6f4d6300995d591f5e22a7b7147caab1f9c7a85a4ba74d47c7b227326ac17535de039a7e604225d3daac7f75b9544654b73f79f5825825ad6e6c45d13e6d385a644633e1c8806d3a0a551c98f032583c95fb13997e52a6b02192d26b5a47fb22ee52999ec92b75c70774a4ae0481a79113ac72346263e53d5dcde708668d317dd00552d5a40ad47e59a501b10a50bdc52c5c2b2c920fa0e3dc9b8eca54b10aa5ab1168c88287dcf80ecd880a8fdec85b6d63a15b9f453845421a5d9b898e6149b60454828eb9dad6442ec62ad27ed72215014f3d17539085ea3f668b0a9183ab96e9db996801c72a0920b02a249911924f181e8dda515d114beb83b075ce7e7a0902a6971e31420ee4921f5cfc123f29dc7775017aefe99a7e326119cb925f6f603ac3bb137313609498d5a2000c426cb332c82500dcfd80d70a5e2d156844a683b313266af6d60216bd9e9998bda53c0028f387ebd21a5fca6c0607d54b1e95724746d15f25f16e45845926a83c7598646a0a0d3a8f8307c1a401b823f1656ca41e5499684150a954318da076af9131fcc93361d9bf0ae035f617e02cb5e1224a982b3591b24013ff80dbb61160d2ba4ce85a4260c8a3a3218e0ec869796b1b04a82079cfc0ac26da03b04a11950b2a3331ecd0026ea9737bd20e6ea3045b56e3c71278d2a8c72c8014b769da9319e19dbe762b1943e5a4be15aa84d0da831eddbb10bd05f35d55b6211d02a61c5d9247e69cab6c112acb568ea51f345a5bf16f84782e04220e217722da71d634e1a68b796376c8a49a965c592b87d06702fe42dac12da108d24ee42a109e8e4cfd3bed6ada5e3de50301696bab0564292864f1b9f47d5a53cc0c5f9cc3d57051065fd57064d08764892ef4c4a92b475184a4cb4ce1f3a0e6da0402414db5a9db34632b9717586e799b06e870b81a330da7be6a45ddfb000fd337a24a29e164449f96be015818c53aaaf09b9a565d7d9d735b9493885e7cb49a5f1985977cd6824fd62f51d2a1e60289dbaeaea148a44dd8876cb58b8ca2f29f6455f354e4e76aa13290f2979b78b22afa0e65c696c21497e94bf0141ac6298aac70b5ea843848a2264fc057abd3593cb17465e06d41731b7153b71f8f0f7a06d362fbd09845f0815794050460b41ad003c9396a86eaab240555ac8c75f85c0ab972c1a0b94b67b1aee2fa3", + "slh-dsa-sha2-128s" : "0x2b2fea4a0edd5dcc2f653eb2dd000aecd87deffbce8818aae72d7862b506bac7" + }, + "pop" : { + "falcon-512" : "0x3968f2999d69a9c28ae792a90a47363e689bd4b349e75c5f837181fc7b16d3d04489bbc32252d750d750417770630c5b65b88cef918ad23e33892a58458e9e63ea591596d3b898f65e1a5be3d36650c3d9834e1ec725426960d337b8f25878525ee3c3798b5afc9add5bec82c8e139c59738aba9ed5f42d11c886472e81cedfdb5b910eb73e884e0d4cc218b87cd9bcc0bc107af586c65b3949460a25892d9607efa341f232e478f3c9fc2d05f324ee6eb7bcddb72af371d47c36856f488872249ffdd322d3de7d12b62b1ec595187b5b363e36cc714b765f0ad5616488300f6e56811e88e359365d5698ab123b917bb32f56566958b154f6fe052b894ccc1fcd99d65d19e8117a8ca4887e01f36f181461a433fb9bbc1790d8b14e34a097b149f0c5c075ecae3501944935f3caa76f0b9370ebe6b90390b4d059f9029ee4af98dad33b9050877d49c3d4f2beaa4b5861a21af95b8c52f9ab4b0eb115357f5b1d25fb66a36b9baaa4d6cc5a4f5fb5462432bd82734d7393cd22c68f52493eb3434c3b88fa255baae5c46c650cae17ebe59827cd6645c6d377b3cdee3aa6a758ad3a96d103bf6ab8ae03b72b53f3aebdda359288c32ff5461a158962924a97a24af29f6c976762e8de9363485ada49c445566e71ac75379aaf7bf190a81c671115e842b5280a05bfb2ef3724bb74487068d5b5ab2d089ebea7ffb4381a7e96233f0915f295d3b71db872393bb990b9c74717bc3d72f6271bc248e54828a1e090f6da56de3643b083e9695db7d0a32bb42355e245e5cc2ed57354d82220b449e10cca1cbf6fbe796d141591d6f71e4eb6770a3c7dfcee509c3c28f2f1988eeca61833b6681e6766790c2a6f043b4f67e145b908ba7f03361cd9d435d054e11edd9667e4f9d12c2d74f2832b619", + "slh-dsa-sha2-128s" : "0x39254afc16b1dd8d4fbfff766819460253653dfc6a8e29abed55ffe91325afd578ebfa2dbe74d46b48b381e69d269b5bbe24ca1b85767bd71c744065fa47c646e3603b44261731b50f2d20a7a327e98b84c5ece73697739c8057f99d6ecbc54cf891649fa78b7bcd824972f3b27820ffda3629f481b1193b922cefc907bde3ab1c78b5ac97ff26986f3a553556d0b158e936450b4f41bf006c90a9758e0efb327f67408a7b803ede993522cd9042db4a672c89d4765a0fff8c6e8bf6882a8de432229dee08ae13def90f9fd0a40c99f3d93873a41d74871aaf407b0927a817b476eb93c645254bbc77d131e0bd35a2541248172993b1064d5b26b921396c8c1664178051810961039b15790862415338806329a45a266aece5904312afb1cfbeac1e816fb96256fc6e2b059c25af1966b0bf261bfb72afd8652feaedd6e0e4bf829a75188708ac9c623d4d6699281913bb563b96818f9307b66f7cf4aa9591e30c65a0b0885c22b5d17ff610f40bf2e0a9a32173b2fa7d66c14784402885d84f33fc75afd8f743cd10f5ad6dcce5b33a8f0c0365de2ce1b69aa86df2482a2c2d3746c6f0ee4e6fdbeef5738d809f6b0ef3cc703b0d29e93e198b68d62a5be657e69c33459fe4b163a5ecd72a90f1e53fc00bc8c698af7ae3cc5b07e36cb74088066a54138d6df4ef6e159ce5924b6903346a5fe115272cc951e8713d4b6f96f78d67a542c3bb402e69bdb3c2b06e189186c227927b2aba52b29c14e837b37798321fd000ed3bc0e386ad6e5d3a48494f57109322f0a87f7dd752d862dbf772498154804b09eca84c305595cc1e6654b1b2e94d5b88feda176aa144d3895a249754bb2ea99b2efb4f61b828f265638866a49dd3437c70016347d5aae64c79957ed394a85cfe06b10b98b6e83801728645529537a1102bb04ada4226688594cc28aefb24fbb0d1a60c7b826dadd23f301517e862c67fd88d0f6a5ac08e7f966ae375b468e57f01521b43fc24c761838c174ac106c4e1f435b7ead5e79aa1b303222f6b45c5515d497d8e7b05901bee96945652d304ad9ebae2153321885f19e7ad08c9da73ccc22a66c26c4cdc9360c36e307310714ac44c80887f42a2575305ef649378d76d297dc6faa904377b2037618f23b8be496fbc680645dfb80a76bfc060c6b2aee4ac5537e79b7dc7deeca0ca38041dc877b875cab1a297190269c987dcc46ff34e5bc154e2572695e463542be79d61fd5e96a4be2b07f5beb71b882f99de317116fd0b36131a88ff9707eb25594fec74cf76c2859ba6ba2b968d41479adf8857f4b0f4a26f49f0f04fbae0e2f3c2ab6aefdf01855a7a5a4b8bf516f4551a393a33d3920ca3ec403b4d37c2106d777e3d5dfa768614513885d857ce95b9bc3b992808e7ad71575e5e91b8107c5802043b221c74d73c7bbfa7a191945bbc5ed69e20016b16e2e2987818cfa80695e58950b0c1bbb8c474f861d1447161744c75d99dc8240338c43a5c19a8a9f46d80f6b4b6c1cb57f5a3350bfc35a0522f89e3470ccd71a458968c629c93336af6b5b091f569d1fc9492eb6b976376b8ff19ad9318f5cb1361954a0471419b4207dd9dace8c0545e74dfbb19ce3fdb5d456a1c1bff7383c6fd96dde3a92daf0ff5a315714c74711e3c05e0c59ba525c64ebd17fa20f8e143c5931b3c77da193415e6506c2db27e0e773a6a5b5d47ad95760b8d471d06d5dfe000a2d47e6e4e6abe7219750f1f7892f4e09fe88d13c2e3b53dc904ad58eaed856a4abb0c10830b96bfc1b5764f9e4b34b0072be314608fdea8c5005d40ef99806d4d3edb72e73541fb828d4b2adc38855e09e974293de25da6907f5ea97810eff1be37f88ea879e545bb5a70d2187edec81bd57a95c0314df4ad08a864156579d50826d0cfa511d335ff722e246151a7afce94126232f6bac89b13c142d29e7521479866e35b17c15f16ee89d798ee8bfac5868e505031586b81bfadef4aafc2ecdbde649f63e07eb6026d09910d9d3613a9871b5d1523cf5958124f96b5304d5225b4bbb03aeeef87753d0b6ab51a287df1e9031f56f755d3fe5a20f79b7f0f467fa97eac5bc09a1adc6ec285c8f80ec5c184b8b5eae326300a5bce0b1d757c8e4f27ca2822e73745009291f0d9032051e15b567511908f3ec68840cb6ec6018a9a0e30f2e34759743ef0bc9e12c9b32f29f4b70a998cb8893af2dcf17e0eb4f48e7ba367f68aea5516e7248c8be1d1f896d7e8446fadd9603012dc13126878300f0868bc512221154a5104882621f831e60b49d04fa8a701256b3a413b9db4abc5af10a3f8f4c26cd0b3c2f55a58c9380143d835b759bac7fef824515a18d79b6dbd361541047c79653ab4af5473b7b7e67a6755add78d5c14c0a9edc6c7dd42c3d0359d9f2f6961f8930458a34c4f12478ae45c20969c4c18654d31a7ae73902757b3b8a4eedfd16d6c0d22548d7b8ba77aeb4c1e3f3766b6ea802ee9bd92c5f98d00be80c15e7e3a82eb0d100436c398781607dacbefa98c87b3558f363b3ecb1ff313655838f864c1b2e0f6d6369bae4eb8cef98f564108ab2f763e590a9e9201f4e28ef1971438c30f4376c55cfce9b9910f448e31d06c2e456eb798476d56a890660e45616840377ea0574d1c066ae2fcc46f41cfbb511e5efc4b5c60e00e4846e157bbee5fe3bcd77408e3bf721bf871c05971a70cf7a13c782bb4afbdb1ef800a414260243fd828b67f49007eaf60b3e32f4e2b8186291075a0b1c0a528abb9da7974b3b1e8b20d337161ce234db66ce82a88ae1dc880933343961267cb4932b36aacc5f136cb474bbdb5e4c4be4cd74868775d80317bfba00ca4e58a39d04e63bb797de366c45efe2e356672f567ed1f7b2741df7543d6faa7227601eb7cff367d5a1ff320ca71fa2a1a42866239cc3fa5eba400ee4060266a05b8fe1e729908b95ba6aff8fd76b5e7df547df62cb89f0605c1207c869d7214e4e6972ea6e47b18235855d6f28265a737b4f64f0d215943f13d219292036c1cfc46bc61679f034919e4fea80d882a5121cb3096a50f2112eab265ebf75b607b93e14ceda4e08152221bd368cd1503ce375188d23514dd0936bf980d4282eaa6fe8f38f5eb00c34e3557ae363538a2c15b560b9f9322d1f17446e8f3fd317126986afc2c5e9fbb96f8ca7148f53cd75a4b845a28034c8679baf1f5406564b6e4b8fa8e681b5a9d208f3a1b28e84d6d52d4baa152524908d50d71d9c0d11bb5665c8a6bad669dbf303027bd0d534434f9e76f2c300326e25d00bd8d3bb1debe1c3984961c0ea8dc547e8cb83a8f3b044b673f4a08882d103d6eb143a9645a9a00fab6f1167a2c09015d6ff2dbe4aa4025f604a63eb32330f832162b6dd9364f4ad6afbd86c8d26f56d7d9028dbb3051c85a0c86ec0d7961fd6008f87dbe8347fd5ed1dc4c27285364f63650f161321daa83ad41fbc3412bff800c062a78cc5caea330cd58e63ea9a9d6de2ee069732799f52f49d513c658a44a2122b8427a405d706f740600f855130a0ea720f76b3111dafddf0e2ebbd13f60640379993889089ce508b1343f485457cb8e0c09760d3199b4ea940d584417259e41f2c66853ae4235b6f010cceca3859952b3daaab55da14adff0e421e862f138872c2577637b3e12b00384cd32fb3b9c97b0d59e51f2488e379065d79f5a834b076ab5b35fe4c47f2c4c4f43e622527abb8f682fe9f262d2883fce5c9f16b7566c4e078b67611abf72cb8a0263ba12a6c23b1a04458c759f2bdf6dbcc460bc5df48146468073b61aa5ab4a9c5f6c006f54f1bfdd85373f33af29d68580eba4cb6dd88054c81b3bb7f2be8186e380bee0b32a1b84ef6319798079828080361bcc27473a9634c23652ed6b2b2c8f8d5a6721551c780870f1787726b121263e74c9464a76282dca27249b4532e49455a7485e2cda4ccca823f62d3b2869602197a6f1ecfc9d92ab6e231996e603e81e3dc648aebdfb900a3d929502ff0bbb3dff813660c9d5a417d857c19754ee04e50f1241831517fc2d2a71c5e238a944992cc50b7106aabb5482fc7e0a2e6b5b11bb6845f34ede98a18d5f07759e61603de502b5eaabe59a4d2c69f4b0f8e73a9bc85b8950a54bd91bcd0366dffaa0df736dbbbc35d6accb5ab8b22a870374521f2035c1878718d7d3b025838da75d8bfa3ff36b2a10a3cdef1b150abf3b6be95a5c096a921a3eced20f509dac4ba43f2f515af2b65d3b2ebd0da2d7a58500157a666b387b1814d0a3e84f64d0fc44507b0b6792c0c6bd90ab62e0f75b66d50b6f842cfdbecf1fd043d642c5d5b5fe5cb68af62e6e23cc6f0f9b1cf3cbd0233ce48a996296b6609be1c9e7a027cf76615dd11208f1fde119099352cc17e1c9a22f4b71c3e628d72b620f3cb4bd40e4294ff8dfe19480b5b758b874a737e6092254638a222c83aac52fe0f66670454c313adb0dba2b4e95741d27104bbff159e999e23103c00470a7feee6f4a35f764ec81a8ef520cf1e8623fed3c06b63c022e40e7638db269b8add119fec50456b33224c14486f705e5b3339c6f38aa4ba6dec8b098c27447185ca50cd70772e5389ba703a4a9f8649946671a238e1f005aba0b3a18c8ceda9daf4cede6782d27931c638af3f6083b2bf10ff0a632ebd4dd528da3aa2b7a8bddff2901d902b26d518a058921eb0178ef08e24f7075f77ea0c0d0e72f29847ac7826230c0ddb5e7fc8552ff92c007e9141f51384a12e218da4fbfa9a7a4a1bf266aa975949e120db1b6755ff04adb07c66ba9619b40396072e7cc01dc3dd56f7a2f928a82c8906be416f6d5c41354881f9a6c66a927292f8cc2887aee86e90da32fa3bd9d72bc26dfd334f943305fbe4b645df83fb72c565f61860a480dc0f2f3360368ec3715c2c4a312bd2653051c8f29fc14e0c3d1f515e04b75ec02cac2aad8d411306969d9a8da08c3477eb6201bd54c6ee643d87a89c78a5c8ad48590b7a499cc44ccb812866a4c48ec2da5f1e0aac741464b26988459f3724ea4cf103c27e4a43e5a5fbbf94908878894f8b62995aebc20e632d924c82d9669f64b2093cfe023cdd1542c8f9d19e5e7ad6ed7920ba6834cb0cb91bce48fa94be47c1833c6ada4090509ba38defab7d56dde8168bac8021fb5a2aead00809f1881d53d26fa9cbc824488e16062fd9f68726c837234a82d518e3d10c9931295d20d9affa5fd18c0b2dda299cf1d6fe0c6e6959440af9b397f3441bb9881ba1d2df295d12686ef6b0bfe52fb2bef06620721374328551040d2fa434b161648b058b74df2a7ccacd7609b93421f44a136079c79a3e63bd3b4f79717f3c121ceec2c3918464f5619b1a2e83a7709fdf6d1004b0ee51c73e740b20e329847304a82802e02170db386e360dc6027aa7ba1ee35d7eeddb41ffde093073d92899d568bedee914627c8c48427376e7b5d5ebdab30e58147dac5638090e9c71921431f025f1d4e3ba3c68437555357c651336b326c9c519544363d5f4faa91914195314cc873ef97068d928a6b4e5c6ad70ea7a7b09b3108fd5696aaab800eafe463ee52a3f29e1bbe8a968e84b762484a13714ee260759b4492b749c46ab6603c13ece34e301c13bea57d9de706edc839f55b432b9299d2d6643817cd9d32d0d9ddbdbb23acef51992b62db024365ac2db4c432c531cc1ad18c807cf1ee39dcd842aa9a09b232cfd09bfce33277a8ed09953ab3b5d24e5a8a02fb4a02ceed705185880ecc47cb245665cf640ded4015b4afda053a5d1dff77e390c8105961cb3a954c28ddbb9efd759080dea99e94e69b03b928c8667fa4797dbb3cc6ed093bed107c817780f60729036f13346f05491fb837ef1ffc61be7b61d0b7f8dabfd49ec24cfcee088de38daaae6b4e740ab4faf16eaebf89962331a99f17ca5955b15dd1fa493b91e7cd218aebbfaa7d348b1ceea26951209137448caa93fa466ca32eda497a5b9a4fdbd23a10d9742e74c511a1648d00bca3ca23abcd4f18b7cdeb362e020c73888b76b83e70d26ee358c06933f77d1221a8ff0647200e451e971fe45c23aff60a88baf46f59a0bf94e99ab9337b0336ffacf8e79db92067d2b744134cefac1241dfda288f5c6ab293df7469ae0926c166e999ac99c8871e571eab03f82de73e9de2d8f4deea3724f795480cd47e304f27f2567cd94cbed52e06f1f1cf7af2feaeba8b95927eff950bfc26aa0cc3e46ec7bd5d4aa43374500f8cad4f153cb586620b9a0deeec2cb12ba48bbc8655c7fa70ccf94aa6891b5cc8a3c98b482d906d72e9d425301d0cc6fb4ec9c4a5bcdfdd74fdcef68f05fdb78f2a1c5ae32c28c43ee900738f31ac1fd337ce95966b3f557a727e0c3ada6c6aa61219d92fd5cf68fcd9b444f6b4fb6178b9c74238d2575f442a298127f282b5ad416cf6a365a214048489139918f93ec07f2cd864f5482c418c7336ca4590a7da8a20d33ffb7be19934c80b64f4b982a55b129f4896685534b83251d54e46a2971c0bf49ded9b13a77ade3de9a61f2204a46ed9cd7bc444801381994c8ca5a66abd70936f8d11e2a2fed78fe9fe8889dfcb979c9a3f11f2c2c1aa371d1e98c4fbdaa95d58c6b22410c0d3d3460077dce654a1decfc829f117bc407d06ad9ee6aa06bf818ea2f25312b1cbbb5de527ce9b5c5ddcb0df21e3442ffd149b24af5e59eb4d7e18f68b02b26e57fdf23e7af4fde3e672990295c37186ca67b2abdff6b46e1ff106f041279b61560ea2eae64649c65be22f9070c3239b0cf7929b0778c43dae5391d8d3b8c335ceac672376588a34d3b9c61833578e8d5a9e6e9f72748d860d7af98f7616431d1ae1ca20db3db6ec99d59fa98c25a1cf478e1b4a643c2fc9d3e482d56bedb1b7a1fc9c3e788d9de281c7356b82e3c9e1c9414a0d14cd07fea5364b6a22dcb3b69b6f447343822098548e3ca7e88a83187db145bda17f7c48cb00e6ce36d53e2d1e4c169569ee3fa03620a7131bd5c033062bb82cedd0d0ebc1025937b2d85a3cbcf6ef4458a57525a66244be3a62b582bf2b88549431cdce09f6407e1bee83d3fbe205f585cc7cf9c1eaba047a01f51e13bbe0715a9112c451f1daf6074a1af051d8b98863b6fd03238ebe295995d60e7a26d2f95a2b4b0d617af299133bf357518b84e942bc36a88b4030d91a7067e7a2ed7bbf4e9abda8c6887ecdfebda04dedabb9ea833123507835e0357ca1513b471da73102605ed03297b5e3eb7d3bd01c670f992a7f19962b706e8d0ae0f073638d633b34a1229b6979b8e90f2a536cb6024c644e1f7ef211451dc509e061b294c28be188c96246614a1fbf52db8d9a99f7100c878c1b9da98ee9e9eddf7fcb386061ddf46807c60b24d665f0fe7fde205f91021bfb87c8f4c89b5da254d185bda9cc2f48303b483ecbf934575aa45d0d58cea64662f0fda4d97790bbcfa237984cfb45a9cd611b533dc0ea997d191d14d307c68db318aee8f99544fd48fb86f041fe249a14263e6657a5ee732f7c5c9c97a8924bfdddfd75dc67da65042e711a3cc144f46d10a1a9fdac1b1b321eacd235299a1fab390df24acb739d61bd60c6907b9a3827958198e6e2b5a1c90cb9b44cf8db9b6e45f710d7a39c230ec9cd855af797555fb1d73492e29d17da0b6dac59c2ccde0715565efa1f62898c9b26438c60fc3b9f859bbd33a8e05e1d3d5a762bf19bc7c32cf7d4c6bce26fec6e5c8c537608a1c2aea2e1e5cca5bdbb45a8ed048514ef3d298e7dfb9c5408d941ded6220a44c184dc90c922a3ae31c9248c5f31627389770a59f9431fd0139c5ac0f08f69151a75a5a6a0355b7beab6be9829146ada371aa5791c861dc77598f09448af20deca73cf98a399f35c36f1167ba426c6cf4b8b9b8a22b961bca83e45bade098e20782a9f70bd54fb8eb6eda3acd3a5bbb553438cfe37e1f7fabf15729d6bd562cfe379d586181b71b3d3c4b360852003c4363a8d473198130642972eb3434c3a5ca9b497ec7000e821c3419aae75866496bb1b0a9ac6cf92f799071e06809f1f3f904cd18f32629e2093664755803f62f45942f5a358cb1e925a8694998cb14cfc52733c356cfd739c3976712ca7b007e08841639da14e429c102a8fba3191cd36c1c56eb06bf69afe3f2afc4ebf137c617faacda6baf5cfdcc6462ad02f63e8a5375288d0de5700d0f9f4ca82a65bfdc405d3f96fc663658f0aad91305b86ead3b21bd6561a40db0f2c5b0937ba5e1ecbe91e9c50b1dd6bd83a8145b033a7bb6a4ad4896e0ab160e8a52d39d2756b1c8d8570f7081ba217fd8803954290788882befde24b4971723b0ecbeea14d23c92eb6a0d7022a84aca072729d7ab1a88927a25f19a2086f846a44a2726ea26cf1b69c58243e219c756d06c361e1e161bdff0eca10bd30d23782b3374ed421f50cb66c9ee063ca622fa78c8cede932848f0f8f91bd7a0eaa324377c109808bd66ff463a72389c4b49f2e349be70d07fcb56b28549fca65ad853d70df66b587390febdf6b1881cce6c1ca9e9fdbc9f1ad68ccaff97234325d3c972b57ae5723fd73f975ba640f90e717e9284c111b76367c6797399452d37df6b8b8d22ed5e7dc1481a415fadc06673ce4eb6435d31acb6af68775436629a57db7a0aa192310e8427468e8e310bd3426b7ffab00110324c0c7e04ac6649b6f167fa8b928b3fe12ac162db3f2bc355295650fa2d976c64df69a8017034683bb4bd4baacf1cd8502dff20430d48c907107acfc4e29c584cb497b7a471fbde90aabab25290d853b8ebd2c0d49c2c201eca42a68a15879cf391671946319427b237472cd35431128ea7ab6d545617321592c07b09fff93e71596da30689b3c15368a1c3a57890e0ee5752bb4356290024fd1bd2ebfa19f7f3ca57c36d296ae61ec5581ceb77618dde2821460553ba0dd6dd68026b911f4af1be3986198966f90ab757aca2f5143f1937d09c3a08047b797781d1639db4030f9e59f8c5b71a71787cc84ade5a385ebceb1946177a056179aefb49e1f69ed0aa67901b2d6b2152b6c1fd1db67f358093180fcb8f96483fd35a87fa14825d3849dbf479be493bb2d1c639674bd195b5ea3caf49c2b78eb0e953dbf8fb909910c87efc6f9b6592c020333e00f83bea4bdebef4e107798f9a8e89fa5d063d69f88f31a53548ba2406407251f18c3d1ca6e392fb81a46107191e6d906b487469d0002afdb0f32c39873872a49eee649250d4d5d3a7687fde2952291c8ba78fd9b962060df12d212033303691b05eef0ae0e7f31b7e395576708d211d70281e9501618673afac20a93e82f060135e51b29132a0c9244551814bdf399b746651279ed135d162e366f39d40e3a49e907f72bf7c66fcb4ba75b1f447fbc3ae3f390ab4c10963d755ee188a9b0129416161995adcab12177976208e1c93ef48d3ef55ad22bbd97781dc7035c9f5fe25f47b703b1ed50dba311d24154ab05dd88ccbb5a8cd96d4665829c4dca5d5b22affbfd7e842ffe5e87bf4326f957392229bbc893b1fde9e4b2191a3345490d418e9298179661104f70bf6cf9a0094f9d37be98b82d98497573512d8f6fb4b0b0aa334a9120a5afef20e0379197ac7ec78da3688d2c9b590e40fcf866acbd1d857546522788d1f3ae73f330aad045c5bfd59b8a399ae626fd67d9797b32d6a279d42eec3ec8a2888258728f7c329c4efd0f71b730e57f9bd87204d40ac1ea6032a4b8ee3a338aad58c2f3c9795819af4cb652dedd475e5662d9c0cd23e72a8f4e86f1a63e5683c814a99a6595b779e5d715ac91ba62c1ae4ca1651581506ba80c2ee0d3f97b979758cfe93f373873f4775c882fb3b5815b9274595bf50488b935c82e0fb4ae4d078d30bf0843c89e484bf5a24d5de602649e336fb2b79da6131f96ef1b5f82cbd668f4599fc9a92b4e9ffe3468e39e4c8bd90531952327da74a522e479c66b7b1554c9a72a2e6db58fcd45588c9ff4f48be5ddc994913833b668c275174b9d862dbe8fd01535ff2bcff6ea030842ed3a4d105ba07feb0098e39f61a4cbc06bfa153c598c10c5de57caa413752878c7599e5db8482b6f275db2185c505bc0c9670fa0afe37031eab994120acb935bf9dbe6cee6ba336dce1b44732c4267ee77f5db890041e547bde486335b9a0b85d6640f6534d84b3729b3436ac1c0551d687ddd8da9abc18689e7e932d5d0e787ab5ee5f36eb53789b9ba2b71d7a4f7994e56b056d4a042bd2cac79ad12d87726ef8cb97342e616269e9f87493e95dcebdd91763b134019c0a76e8b12789b026ebd1d9caf1930010aa1a5c348ae2e6273c885ffdf8731b34d87c293e77e9ef14709c2e3b739dec85d5bdb8f5ebc9eb610240576435c8d222ae0c8ebe5844980560bcfd45cacff143a5c208add9011bf9770b7803fa90bbaa10f58d856250b37e481f325c538e473dee7863957121374bf12172321b9c99975078e43a0e1e40138f9c31d6dbe5e06fc881de840e64899c0cf342a806916147de3cc342832a9514eae9550d2cd1166e823f404a05b418e5f9ffa84352cba3007f9d37e5b00049d7c099346257fb53e86b919b45a9c6cd8b67cb3c4e383fcfd5789f2cc087d6b459e2a7dc96d08c826c43410b3df22021855c7d413d9cfe7ca79fa860918a74941e08511595fc9a3bec2e2e0c6dad05781c0a0d6b6609837ca8f78b0368b1217b121cd6ced8fa48dc131709434eed65523269abe95f338bc5e3fce0046ecb24dc1ad8c8fa68b5f9263361e4952ca1aee9229811a5ea85c9c4452235639d1eb8105ff88c048beddc0f7efd3690da8b9bbccbbcfeddcf3e3c9e3ea7b46a5b40098fb85a25842911e42a714c2dd0fab403340ddc1af9484843b33c7f5bed80fbecf68180c9b397ed85c51d39cf08c5c0336b149e6d1747360541e84a93224ae88c1b62384b938ae25ce17d656b9ae52fcd9100a40dd7793ca67357f3fb00621863a490373f0db49b1aebaff7284d450922f1e5243bb7fb5f7230dd7cb8db1e08893696908cad4a45711eadfce8b0b42a9aa937cd011bd5c4a8c3a2ec771b3ee4959eba5e4a17545d1c86acaa4805e4aad" + }, + "ecdsa" : { + "addr" : "0xa16c987b93f1b3c51fa8a9833c8932eeaa566ce0", + "claim" : "0xb0c578f123a8a0c96354a13f98c4c2cb8a67541f5ccecfdd0cb6e61aa77f7768157dfb3ba59aadbc03350577f6f31b4083bec30291a61ee98ecb3db2536f788e01" + } + }, + "2" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x39cf29ab9b80215a9947b975d8c6097e53b39d1483c885ff7527d7fccdd2ca3dafc32bfbc0a19c857fa8be7e52e25064d69c3b7dde6e77eee96ec77011fee440c8bca99a45b432b5468715a0aeff9d61375393b3ca40b9140e1a2bc2a52c3c8a85292f4c9e87d1e0c12fbe4d7236ba7921b8b83bd10ad554179dcebf0791c4b593c8a1bae836883d5094499cf2ba41b00c8c36c77a83466c263a0b01e773f08ec66d46e97caf0f9785943f98e159e353f0d91d5ab526279477a5509129f7a1b8b07ba82a1ccb4acd5be5e40de088f2600e06a8bcf35f54663279f04c74838294048a8f8692904d4f9eaab49635068df74d1f5ed238f6b895acf1364544747bb10ce333a957599594d766ac5d1a3ceecf8a6696cdaa95f824ace18cc551e1f4ec12f345283b366314515849feb5bf81fd203a6c73b5bab7c01b5c1131d442dadcb6bce32d6e13c5a3e77b66eb8f5b2912c0938d89ed9e20a455066d151a973e4509b03f5d66ff7ffb8b362e0c09b969e21173693d9977ba06d48bd3b85657119f9dc3533c91965ed81d1eba72b46cfcff285131f84306fec7b271a02dd22982cb6399a9060d286aa18a3f97666810937f8a5667c94959a4c27299d55e94564dba27ea3d98cb3a9db6197c842eafb237649de3fab72a9d56098b5c1228e70dc062617a8a6b44d46b7899dc1e53a77b700925ed1b438c59458c15e9b91fa2c2164b9661a976720eee2864efc5363d90beea9789a6d3b0628bf4695a853251c7f70ba1ebcea06cb35cb47d72d2340bb9412edc49626d6c78be644c4ab1c539d1469d1cc63f092ccac45f66917d89274af9c84d7f1108b1bd738408e499efa63f1a87c8730400ec19f9a59d241032e9ab78670e85e8da24310b13e89ad521ad4fae3deb9d438dd7eac72b79496c7b65061314ae", + "slh-dsa-sha2-128s" : "0xb8f14bb6630512c51d227c12a04ce7b316bfc0d255f5a5d1dec3e1ce5f5cc92927070079430990b4364200dc6d389fde89c78649e21d8fd3a17dd2de7444e77ac3e8c70eda15f2247300fd8e37e1e3c2afcc3e8dd674823106b4297ecedc924ca948a9f80afd7ca1bb85dc0e60e1ccd5d97eb21062b2fabfe94827b4ce5f0e396e5a891fbfc61bb827cd305012515d0a182f163c1a5ee3827aff289ec526cdafd2874c457947e8a6c8332ee7172197cde7388303992be248892e727b559c0d74a2c65d46b6686a48f0b814bbf031d1bde17f55b888e7cbaa7fd6dcf428b13ca43f77d80c3804bd2840e5768c2aa17f6dc773b71f83eb7803fd9e1f1a2e3e158f7e823e9667cebe136d333316fc670442a8c662930a00f72dd2dbda9b19c3dcfdd09c60ba55d9ffb5d5b3384d719d153566bc0b6da89d8dd869fb8c4113258014cd48a67a3591d37f9ba767ad70a554ec98bb59275ce163364915051411db73833a88cab0d3fdb7c491b8580baca2bc44a7bcfc3a51c28ae0a1fc12bb53b2b948c571397a348e664522ea749890d1c7ea7e9561596a3e930bca68377e4d4c0e0301472c8017c1fd5702cd6dd4c3f5881ec4cafb98967a57837c3869720268b61f37836d0c9c5ddac850a835318b17758a6f97df4d4966d516ef100b7e61271c45aeb89242ac7d2b8ee45a08f4081727d54cc08ec0f8af9e16bdf6f6d6797b9f5fcff9686f15efc4e8e34b363d562dba239908a3992b3548566a30cf04c424383b5bea0a5011ac620ee384f6363fca87daa30fa88763146bcfe0a160c071d79dd8de0b11e034fddc032e522b7ee47f318cc6f3f2b6c9b6efa0aaa9b9a9c1243750ea31ac01f087d5f4f5fb97b8296b0953a606c49025f5bedba6773804b15fb3638e72ffffe38ac20486e18eb21059f419b0af9071be12e309a953ac82c0a5a620a7b5c850027b39cdeb5d728522a15c1557e4ebc3f4f09914b3a99e291a814504ff7171c23eb825d2df1bad1ff29144f8a3babcc882258e8a6060272e95c4393fed0ad86698554515e77340ae806746ed859ff9730cd718c767e320ef54fae5e0cb8ba331bdb920229d6047a6db8bad147b75d376c3e72e06fbc6a542f51a312ec831c704099b2eb0f801b68631b464ef484eee077cb7fd9ea951a0307e08dc5fb4ed5a6e62048b010ede39efc5462ccedef2a4c61ffdff63fb3aae19b28ca48593532f051c716835a61f2fbbd5c25883f4a85b48d81658b6d7cc4227f90a81e961c5fd8a388c9efb78da1c9bc50d53a41d2d336232e84e3eb3444389caa3457ef9e3560c25033ed45242d0b4bed69fda4a4f1695830c843e63147b6c2d3f9bc2608b0671e59d827dc1e3876e5fc6075bcdf95223f73dfd11d64399333f7bebdda7f5c2838c072f7baf753a36a1ae8987773fd0ae5acec41d7666c84c1125190f432a4d82ad9b78e22d6536a8135d80a3d1715d17a673033d37104857278a75c2cbcbfe0159343dad8e98e42874e2b001bdb469c9faf79759c4cb3e3f2ccaa16bb29d3e4dc81627441d07469dfb9f545ed83d9c20ce586370be092f3a90b18f30866336c6db9d53e2e0fe1f98c655f1961997e41c25089a518f05270ce06cc115b6c2e9c04f6ba9bf7ce3abfedfada8580ef89f2fea98e8a19c58a0f3ffb89dadb05dbdf05c44b79ad5e1d259d5aa6086cb295341dc40ff800b460a5ac602a687d6feaf66393dd2be22264498509c9d39033aaa22eb248780a3661ecf0a05cb6629ea3d91eb0f68465485e8fa3fad3fc4315a118e4eb2ebc6e9b07284697cacf0257edf67c20b7b0311d3094f4647da567385a508addc9b25a66db1f1544dbced87886946989682b2f2db1363dc635e4cc3c67ad83bd311437d22d8f60a2cb7833c952bab293c9e20ec2b44e1f1f42a797421eecb2e9e9f5e47105b9e7f5d7bd796c360ea39dab45ad701eef0a76b7773c55e7af7909d456c213b0db14f985d0f73652bdf39a6402cee104a168749466e477d8a399469992d65656b607fffbdf73ca1bf4976d3a0c0c64cf5d260d6d3e0198f0b86c451b9ff5840968a2b60a8ec286c81fb55741d7bc9714177c9a77219d3a39dc1d6d7adecb7f8f85af366ed653cd81408b509d0f56b5dccdcd1d982d115754364ca3f14a6f100c54ed8ebfce6b0dc3de4435507acb0c4d974ae3f2453867a70d5ba078a17db34844ea410af4f842dd4a9879a7706b18ddbf94c7b43ba137f7f9b8b3924b4d917f8e0262ad10d083c4dd3b99b70560377ad31784a587cf0ea1f99d6faf0f04a909179f3f93c96aaeae014ea1e74d9d7ccd0eb5ab466a6caf8ed0fae75cba902ae05244e0dde35b8c0bdbb3354ac60875c454ab11f49e47e2d180db319a113a5869bad3ef4580bc7402b0d6bac10a7a4e81403634fd1cd272076074e714b14a6efefc902126df6091330ae387e161789198680b0258c341b6f3ec0b09c7674e9ae8f88699fb7b2f88fdf2461e11ad483ee11fe4e3ddd48ede0ef0a1e54a318ae12473993a8456dbbe8b5ded40232b8d2474a90424281830da7bb36091ebcf591947b04739846d8a600946c2b892e2bf3353b09b08e3a141f33f6ef83d022666d380da43991d3eae4e62dd3f9432678e8be1cd4b9569fa2fa84e95195449716423688aa920e2ba1cd41cd5538907ebdf3ff15df27900ba7ee09f3e9194e1d8cdc0703d6f38b2b887a89b10787986013f688f9a355c6a362f4797576ba022e4f1d1d74e2e31dd98f1b617b99516c944bfa5bb474857fe74a09976cc29c71a7f5b6fa529f3e0c00c33c2f0700064fd507461535a35c0dbc5b69d413fd6ae9af49ccc5d02d7abac88ef02bbd40475595176fa3b68d602469a89afc03359b032fc1f0ae255d623fbf9525bfa36ef98467d5eb2f80cf7c72a04b557e40995471c7adb8701409830b232693bf2fe905ef8b1a32c4b8fe3300bfc5654acfcde685547d5c1b0c213294235b31417da91464619822f3535c93549ab4649a59dc3620e7f4a3327c0e421c5d1a28a2b4c3ba8ab02950631ed54690cee35bc4147aaa8477cae505a1d1757d85251bda5fc91cf65772e1b543bccee1a2cd0c26cd33015533a0e72c815be8634efbb50ceb0ec3c8a3bc6c84638ae371809ab87eec54b934343ee9ed1bebca488d9399a1e347b1647d108652243cf2531a4685c282e0b9e526688a3ae1d10060a78467e6804b705c143d073af664cb72ce657a7d210240011d539cb3144e9411758a211238a872eb4be615d4813b64b8df446d2741db7c9f0af2cc4dc83c0febc41adb5d8413b7ce54ed564e899c0a5a4abcd1cb44c84505915afaaf93751b11bc8e96610216f71cac8d3c9ddb7282f0f864400bdee42c3e5645aea1aa5649485435ffec9e43d43e4341d967c9a89eaaea9cca12e2089f3de222e1a5034baa32372582023b5564909fbb67acfd2a40d5ba40de2d7a31cddac717f5702648c0346e197f99598dc83edda7ad335f9e711a0835c75d4c32e3305e4db9dfafe171181551a8dd5bff89f8a9fb819eca7a171d1be47fbea0e2838a1f1d01f4e68c3d0045da0ed7d3689c17319ef5f5d178e6cf1628402c40d2095c91f7bfe6b837d8f86c5b2585653beb650fc29096f13617ce6d36fd6107ac83c7d7f5a8639e519b161581dd58018ace297bddc3421d358be70179b14bdff66f229a50a329bc30b054a95d0f43e7914a9137055f4dda000357fdb5e2afa3d61b9816c03697168cc022f0418ee5ad7aa71617103fe17e7f47ecf7ef9bf779f157eea74c3caf3821791ab628fe8cba64c12d3226877c48b746d725c46b0ccf5a18f869e6680f1e5ef3c5e7fc2a18915dfd8bc12676ab94ff4401ed22133ac1e1ba0c0352a30156bd80b63eed4a7fe9c948921e1a9843127f1191751312180b75f06e33694853a05a24ee30cae0b02a0be49db16f6f06630ced9b5e57cad548dcff70b1620ffb17e8bf8c247a962d6b2a6fbd4fb3c89e3aa4381e15b92f7245ea700e76ba99678f4b0c998ac90bf3e4b9ba5b52140e15e4b5a38e25462c1b61953cc2068d0b9ebe014db6b8fbdb2fc05751667659f40ba1cafcc2055c364116a058e286f084c2bb73096d46f6736de541b750ad437c27a92ea51baf3478837e99faee73f2a2745918f81547c45f2b7083befc6387a73366c175d2be22314a1fa73f11571395c7fe9df062740c58b56b4982240c63b8a0d5886a3ddbf6ffcbd705d3bf3713a0e1b74497b8c1f34e204ad81b63f27cfa5215d275597599f60d02bbf6cbb229088b56c96d73d09fb3c36fb3f05f4266221984fe8559619da67bb7314ca66405cef4bd3086940c4a7346243776a6fa128cd9b6181d8f584f482c502fce3339f87e4fc5e3593b7a0a64c19fe76c51a29ef776e478ecdd5e093b2aad0ab3cff0baf73f4e09d63e9ccfd4aeaaff147ddf2e9f1e3e6cb3bc9757c57072a1a8076e179e50b0a03dc94de86fbb99f0e92338f26b6ed7c863830f325d3606189b80c8df5a763f422f26436f753168b53bce965c562c2554081cb4eb42d48daf8573206f818ae66d62bafd151e9784d6a9304996b4b82cef081af9b106b42ca4b6b0b2401dc21d8875f9801591234dec266f1f218959af60351178049b487b2784183349e0a7ab361f575fd0237d9f483590a23401f9085b111ac3d740ff4c3eae0b7dca3d149243e3b1fbabdc7431a579652d522bef7023e1c6579d2f6852f1acb9b7f20e3956af8dfbf14617f14981f40b1b2430920be14284dd5f2771f73091adaf9179b8396c6ad2a3579befa1e323177fa2598a3ef80ff5785dc2677f87e9299c6bb16ef2b2080e91f6fcf1b9189e256985e631a05cbfb6427eac995e683f494751f7ae888bb0dfd23aad7fbe942b8100d4daf62b6b8e4d647c818ee7edf0d4d64293edc3b7b6e08a8b9b71a7c38f55ca76593d35770185637fb473da423069de005363ec25235ce816f4df3070ff567f919f47dc4fcf57c168032ef8d7e3fdc5a6a1dace2513fc24f0829e8e4b964d37333febb1eeaa7b81aafe30d16fe64b6ac037ddec2dd9b372c657120df4372504cd14be89d3719c5aa6d545030423fc50e277eaa401e8bb02d70b6614e9768a7e42ba9b35af8f9e3f7dcf175609f7b91a16fdcd65a151c0e85d92dc8a06cddd1ff77d150ccb62c612c90abdad156ced839e837c79b79f456156a28c45f3287cb86a9ca664a13313332a1ed9ef4b3a3df864eda7bd55a6d8705567bd8d1d732f65a324aa6c77f6dd82517d5274343a4e16fc8f41f5a3809e9f2cdec445b88309c9d1de684032a10f8944157a3dc3d4d8d59721f4f9cc0b0e1e7a97b7b066523f7498e0902ac5aa462d9bd096e13c14407bef96b59297391032a4aef8c543cc8b60217ddd5a5d815c4ea1e21625b52e2323b42b4851b0bbc461abad9b71805e8b1b2a16c811be69140d3a6d32d7aa9d5321adb57570fc2e62618cac60a4cce2b011a4c8ddd4dd521b6864c7c4562d8270dbbceec91bf1cd7be926d6b66bf370839bbb22856d1f0c7b2b7119ecea6278793186c50b045d09a1aae7c5bd2ec54df1087cbd1d8094293392605321bd39ffdbbe510f0e8760bd35074c4b0453604ca1f5bee056175ae7943183212ad527e125d2ae113d83c5a4ca1e63e8d89ba07ce8fac8257fe1c603031a1fe2e41bf922d7ffa0a610e0b91eb8c3147262fd228a4ab54fb520b49c91ca268d3954029be8677229af3ed232628e7d3a752e66c2171d1746b7e31d2dc8d1781af454822f36b3ff40f7683fcddc5aec46b13890a6c7c9531f30eeca923f198f4b57112a94e34673c88c70bcf80ee115de27ef5268a753c6f827e71254b5bea2a4f27c8e0e2d0c3f72d051558700196e80abbdf452bd70116c24f635d1850f9466282ae761f1a38a8a0fb5a02a9e1b99df55616f0a7c031fc34f190dfe5cdd4f2442d16ccb49187823fa90f3f87dc0966c4cebbb7472d8184e37522acb165f5d3b508f6a62623def48edddeff361d5ff37fdfddb2b324c4f545bfd5cb98d258edee6ee6170bd41135ddf4c9e358433330a5b590b25fbb276a2284b2df18550270b32b65d9b483fc42e9610537f27715e9f451bccb73e1ce017aaa39b65db4e86c86a50d0379398e57e0c34fa64f73acc0d2b4e953a89ca2f8c01b10135c2af5c234dfaa17ef97941aacb7cce96cd05fb246b76f15275e4d2a8545e52726a48f269d1a558aa12588cc78919a5edf6c0e9e576ed58c92b67d00e4197905d74dbdb3700fbde85d26de20cc652581608ce1d1084b507842a0373fd8830c8d3f68dabe93fb19e3189e486840715f92ae4d916a9ff3424b45ded24780b31fb7f76d506339d9f9a545fe0547c675bde44a3b490bd3b52e3b30842624ab5e8986161be4ef9da785bf537954d3c716da5597a859ef7adb40fd8030611d3ac6581e424ff636aff72f4dcd9f315744a3dae6cd386b2f9945140071a5a49f05e8f979115678eea7d6d884a92e4f14ff94dc102355c3aaa93e02723a92417ff49fe8cdbefee7e6c5e952ad240fa7eab4c7941f6ae8d2a1086e1f2ca73eb8ca5fbe3588ae51bdaf18646ed04bd185a19bdad69394ac15d130297c0fce9d8fce8ea2729dde117facb274f9865ffb4f2c7248102dbd96767483cb7fb764fe667d484a3143db514f08577cad8bcf695b0d953e2a3c8710d8979952929d59c8b8ae62ff678815130e68c85d4714e832733da40b933b7133725a40a12534753eb4877c65fe4c75542f28d6f9e805789995adaaff6fce3929e6027338281c6d0c33aac092fe0eb1797dcd953fc1f815df794249dad7b28407c50371a8c81625f6e7e08ed59a98758a9859c41f7436f937885ad35102b0eea85f3dc4bb9a6260f4e091ed983b1009275d43e4960f9422dddeddd09e9990d545cb67dc3f1061238dffec3ce01376259262b4c32c6173a6f1e239f4502cfb0900bbb7e3b4883ca3c64c8ba91cfd80b2bec6aea25b01124ae11a56ab3e4d448038795ff62e9d5ea5cfdb99c5901a8b8f8731c772690dd964cda343bfd020e920b32910a68b433ddd01d117a833242fd695a54f33fce28b506d931adba499373b0a6182e54323f032f1e7b8bad58dabcffdd1cae831c70e87b2c6fee3494dedaacccfdc93fa23ee1a4273f16adbbbcc21f5d02d8645951b93287732cbca4c61a0d0549345160537501b8c0dfc97128035875e73db7eebec24813d03d11cbcc089929b0f6e2440a26d90cffb6295e8fccf65b4d676abb8ff66709913775e611bc843302a475488c9a9310c0c8885b78d5db5cbb1a919683cfd9bd010cf2f1206ab327122ac87d8e2899cc1ac684e72c91f2d36c7bcc3491b768aac86e92aeb106d22b3e85112a2a871bb8c9d0a0dd15c89d113910f8babf15f27a13b10200ec7078d362773f17286228b0d5a58d6654b0fb4f60c81ee581e507f17a044e6bcad4bb6ddc66bdde7d311810d79ac031086d7b0c334cd3a8a496591aeda23b2c0097cefdb6894b352395dfc79b74a6e4d086656b0cd9c95a48640cc8b3d188189ec0b98c8618b0def4b0175f11369cb2f107e89c4a67a75396cfcbe899334a5d5df5927fef2eb8bd836a27a2834a7ab94813d30fa8431da3178da4ae56caaeeee66cf545537ea260ea6af56d4ba048076ace08ff2a446080a6b688a916a13b43b933e506ad187b1f829a1d54ae261651563874cae19c490004205765b43c3cca4cd8a309fb1bfd7520bcc07794cf7c03d9b1d1ae0cf646534f5a97106925ee96dcb7fc1f74e62c01d382fde19fe027a4b9b7627c08e690abe3ab076f184210acbb4229d483f7471ad551eb614c35e019a8a894b5f92f5371fd6c457f135ae8377c5781a8f3a6dadd2c00697bd8399b6f168dd64b2d2d66d5aebae90fa5f904af9f950f450d8be919ae3f02cb5c0be9687560cfab2d2161db61e978ef46304de248383dc3a99e30573bb66f873ef1e54f523ff8ff79e8c05e7f8143cebd1bdd9d2186ebdbd3b66a0a3dee79475cda2890720ac5bc33f8cd3e817d82bd7c52c34d491e1e9d1d307eec17c0c7679af3626e7b96bbe6ba2550f44de7d1d88b3e4c220b8f185b459f1f882a97248cdfc3d42209ff44806837750b3dd21ac5891254f6c7f6938aef3caca6c55b633770772c2c06734f84612a59aa8cfd4f347267e631658cfc4ff89cea84550bd43699c1d8840655f39e3449ea0d2abba1d81196413302719bc9b39cea85cb7db6ab7f00683842cc480503004c1c819ab80e121d9d21f46f362614a5f2cf80cb2c8d3fda82447cd0155fd7e0ec7589a6df2e6614671071f0f956143c762d51172da46e9f61e588fbf74cb3ceb74bf2ce4ec4ae26a28245f98ae91f09cae77c4660d6dabe7cad33ac506db3000d742f683e08bbd0a7cb9eec72a1a976546ec926b9faba5084e00d66475a514a73f01fbe1e79c0ee0066f4b724e4977f3c1518d28ddc918c573693e9b3fb69b426df0640c5ee42cd5533655f9a82c6843516298d05cd970f34dafa886995545c37b3b939e0dfa3647fb3f330bbd3e69307be3d469a99c24382acdcac76c6cb71decb4b7b11cb5f2e63d112767a7d6467c51f1c0c27900803180337f463e64f507ce245f606f8c1f1df836c7fd02502bd3127424804c69f1d066ec3acd9c4374e857c4607b711f532867cef2c1c8a7d7051a440eb7a8507500d86f11567e2fc812a27e63e5599059af1ee90444497f69add7862b93ea050f100e48df7a578d55b6d6321e70ef7a7625ed89054ab010850912088fdc21551b89397c35ab582aed6c215e00d398a857090e2f7cac21c7b51f34054a45f36157edc603b5d6e76ce20f2dfd2c20158943efd2349560666ca38505cc6af776e0720af2d5f6a7d98f385854700fc0c749dafb597e94d7ac54aedf34840ea778e8873d6f1fd7f53de7c06327d08d34a03b38315efd8eb52683ec1f9abbe2bcf096fa4f379f41e09b8bc67ae4685e68ce456465b38892a238b18358714fee41da0aac8dd729e2c3f17488470648df81040272b2e927e896a4eaf6a11c457938421288ff074da8db60d37c6d022e86952341f2b8d262cba0c5a6efb51925517a8c817a152499ae7ee56d36df86253e6b2d93079c89fc922014b03da3941509e979ff8dede44165ca4cb698924fd12bcb02a35deaad608ff3358d455a49e12e944dbb11ac7e9571476bfa856f3bcf90ca9bac6d57d5c1755104b348d2924ba9b14e9ffb750cafb6edeca6dfb79d73b9839092032035ea700d81f19d035e38bdde754a5098dae1c163f9c473b0681858004ab9d9c2c57282ebd7e57c474b8f7aa091d1048b95e7ef887767f49d2a7f6b410922da142ab8605e735ff6ea9e3f36a29380ec43dd009d98d8c20f4052d81fedfee6cb018b2da59d63b5fc153d5b0948d613415a6b8a2b7338897188582d2930480d897d55bec899723b285dec9abf819ac7e6f39d8c35c484ca13f129fa4b334979bac7715bbf5e52a9926568f359dd3e3e000958b561527bbd52025918ef75228c423da41861396be4f829aa4bb6f94f65e662e6ac91e828d721b741d7333a8e3050f136a463af537ee97af4b42456d65e1e2324f72267c07acb88c91d0cd602ef765f858f69fcd44ddc73dc7c5f8825f3fe04affee769f3ab5486232e35df8fdb75db79721906907ffad83c57a364ab85d93bc03ace8998ede749d88985ae41cef163a3d00f568d43115992362c581745f723a2955ed925cfc7c83c5b5188812fa2dc016dc21844f923b2362afa5ac5f2a6c9355b2eaf724606333f152a01c330ed88f7028e716043330a280c5ee458f2032c8163f5548428ce4c529d487a16ca0e58cbf41dc4dc802cc5ea7fad6a50de0c0f2c2be49c3dd2806911ab1322a30acd4fd8937b8930069fc1a6de86f9ef90f407b2563594adad581b49ad1d19b30ad13efb0ddd8e6f26dc43a36ff8d14d1c99b073f5f1ce8923d3100bb04488b86cd4e89f7b9f5b9cbbb3f86ee03b498ae255e09eeaaba3e4e4d68f1ee4d50f699e460a922e17ea8f0ceef9794e944f128efe835f281a7662f959054aa994afa3982f7a310f46f53d5ffc07884aa91bbdcb3f3779ebdd7f90801e96222f56df39ed9c2bff5d4a11be1da7482835e494b0856ea7c695cc1b53acf3b2e2d1846fff50bf55be65e16548c4fc436f20c50c119ca8eba4cdeda8c7e4d33734a0355586629ca5a7c3591ea4b15428a81b6d91529485004cbbf60e75208579ac354dc705edc7bccb32f23030e733d3f549fbb841329a01f5c5d263ce635a57aa9c69432e5213b155a07c34fe19cee737cb993fdc82ff38eafbeb734f04f58d367e34df15dd6044591983b9b45d4ffd87e2a6b478c7d1e86b4b974991b930370d5eaf5edbb53c3dcda643fb992b5fb0725a8aaf8a9e7c4012311f197179cf1293884e2356fc5b624cd46358d3c7bed1fed6b0305114cd11675aa49c1b9e6569817d34fd60c1105833d78dd00117284cd1722074a60655dfc428072b0d2eb881b93d6c367952a4bd59eb902032cfbf9189bf65f7bff592032c77d61dbf778986b491e297ad0da5b367c8a10c9071b8d509b246393fe04a290b3e2291f04eb750285674ae3e9c7b76a01b679787a4006836dcee956185d3b0ce16c4567fab862d3634b93079305008e2f99ed425489bcf5447699b7a7212fe2196d3999b39191e750fda742c6ffb5509e3806f38edccacbb8bc73c96cd46f0f400c4012f1205813d69a48bbd1f55724c7de917ab56d4188716b96653ebe4fd1b6d5d252f5599f9c53a4683ae60c8c31b86982f32bf73401a328891804daab93a155140fdd41ec87e392c8397c9ecbc48f26acee4e3cc279cfd67d75b5e0b6484274484c18f72c2532b4064cdcc309df796c802c17480428c3b5a5467b38aabce03de2a266c8a22d3b0158beb913d50615a1002fcbaf91edc529b0cd38f35138bd71dcfef1b6b90adb4e68a594de40199724c158db9c85ca78ce3a19378ee90dac98b23e1515324fe18472a4ca93b004295b7abd3c146109da707f5ef37886fe1a8966ff1e96375fd64536eece72ae3433a697492955cb182331ce72e8f1804efee93b1929338f" + }, + "ecdsa" : { + "addr" : "0x5e13d369c8adbd8079272b624343ee1edfa4a560", + "claim" : "0x197e55da82eb1daa23a5478ed230c6d0452d3c1920df15e10a8d37a7430a7f2b69eb474bfe0c44a6e4a55db9d5a0e0b74549c044ae22778ce30f47a9fd1eba4100" + } + } + }, + "registryHash" : "0xe17a14762fb5a1d59a9b5cdf2a25414190c81bf697435e751a50540fa74ca521", + "root" : "0xb78edcaefeec9a1285445fc4fe230e0fe656f360ea839d9c7e31396543f99bdd", + "rowHashes" : [ "0x6134be442a48790413f81ba76b4f258529b161988e4da2c03977b1450e16129e", "0x5f62516dee6156d0814cc7ce084b8902d3acef529b2a68f7217f7d61e576d28b", "0x43231e1c19e422e6c2caceaf1a30094e26f2e1cf19d572e94a4791b332928570" ], + "merkleProofs" : { + "0" : [ "0x5f62516dee6156d0814cc7ce084b8902d3acef529b2a68f7217f7d61e576d28b", "0x1b7dabd6159a5070e03f823f438a70cb6057b20a9585848c2d6841bb1f2f301c" ], + "1" : [ "0x6134be442a48790413f81ba76b4f258529b161988e4da2c03977b1450e16129e", "0x1b7dabd6159a5070e03f823f438a70cb6057b20a9585848c2d6841bb1f2f301c" ], + "2" : [ "0x43231e1c19e422e6c2caceaf1a30094e26f2e1cf19d572e94a4791b332928570", "0x68df066c46127a7540204925b00d14bb19bacb4ce19d65b8d6d0504337f8cb22" ] + }, + "ids" : [ "0xaaddb4dde09de5718a906d83be7db3d58e0ae32a", "0x690a7fd66157e5b777256ea5214f634f61b3a72d", "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4" ] +} diff --git a/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-b-rotation.json b/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-b-rotation.json new file mode 100644 index 0000000000..95d2a953f2 --- /dev/null +++ b/consensus/common/src/test/resources/aere/registry-v3/ok-epoch-b-rotation.json @@ -0,0 +1,60 @@ +{ + "description" : "epoch B after A: index 1 rotates both keys under its id, index 0 unchanged (moved), a new id joins", + "chainId" : 28001, + "requiredSchemes" : [ "falcon-512", "slh-dsa-sha2-128s" ], + "ecdsaRequired" : false, + "previous" : "ok-epoch-a", + "expect" : "ok", + "manifest" : { + "formatVersion" : 3, + "chainId" : 28001, + "bindHeight" : 2000, + "count" : 3, + "0" : { + "id" : "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", + "keys" : { + "falcon-512" : "0x099202fa37d5e165b88428d963d2a224b8fc82ac0e4b3f6a02376f18218d1aa7d0e910e71586aeeaaddc2a557124d6ab4f9a44b9cad02166413cbeb14b364e13956ea326900eb2900802644167639045d76bbc4e051654cff404dccea94402141ad9fd992cef7a8fa6478b3e6f8912886e9edd1b34c29385b7e964f360e0fb1600787934486f461c95d577d2bc74c4dfeb969b4798565ce0225ad27aac052290a44f844ae233e082c97e4498a31117ac9d02f3605c856e2dde6f611812161f89cb3a54dc936ca1e740a6c659405ed586a9a963b11c89b01746325e39e645c72b0c7e4491b36a17d43ffe38d88c601a5976f5a9636c964a012bfbce5f9b371e7c3b38580d81c6e12b8e8f2aac219f96245d35b4f4afa6c94015b055703b8c4dabaea4ce2577482f5b34fa49e56a912dd58d74580b1f94e1d2056594ea64bcdb696b0baeb33c86b6544761a3ead9ba9888cb1ad51f7027ee52b21e46c5eeaf3f77bc0af7efdc87acbddda7dca00b6acc665002c26447e5b0e0bb5ba261717c68dea7f1576462776954d7249c2fd0886f615ad50d53960ca02d581a4a25517c050ed467c7a820bce6250a9ba2e44fd67fb21bac843bb134c95360dc1c50d54b7280a94440d018181a9d6374bc64d782c84471b47287d8ea748c36cb83cffe053cb89a9e4ae71df497871c2a728ef210903e5b7f39550d0331e15637be7e28b3a2fc05b0a9b0c5a06f9fe81263c3a94403d6c288a561288ebaf788bfe271a578a1a795a85e9d452b61ac204b297090617286922376bf0a8d67dd35fd705b81260b7aad30132b965e39bc3cd64d154c4175951704873992a814814dae5c6bc1e89a83dd91144cd1b88f6ce449900058afdc53bb44bcab63d30cbd6df115a5ee8f5380a127a160e922bebf7b408de82514b2e2550a0931e065565e8dfc389d454823a55b03e5c989b09e3791f656794e8379b7b4b1617aaa9770366d63a657f71ff40c69b1d5da518ef1e2aa42366cfa38e448563f6c2db5e3089bad5520ec26105fc885993630c10db357883f588a5a384cd6de688e1bc3b1200f87ed66ba3d4a4b671b2a500a0436e0955904030d76577aadddb8c1ed21b027f198c072f654c3abb086e7f185980f49d0e109c1a82741809f8afd15ee56a887b392c5b172ba659144a7e99e25ba19792d2a3c0ccb481822839c8c70c65c0943a2994ec9f6085b7ac64ce2b35489a72dd6693d25f2329264e063db26ad4495e3ad00f12df8cf914fd846", + "slh-dsa-sha2-128s" : "0x686b5dae4aecd5618b95c3b2a5792888513ba49dd366ac39b7431ef403e04963" + }, + "pop" : { + "falcon-512" : "0x391de378d9ee4e1d810e6ce145b1819f1b73dab7f6dc2468fa65d1ddc8e2a7e6fd8f185f8945700a938cc0c5afd87bda48c1b8167c3e6607ac864bb2b194a368c33cf8ab6a5eebb9a704d4160b068184264c2e59c4f82b9b4eaa9197c3ba5289c429e0f4c548769279daade1286d354212b1ba2ea4818c974c17770dbb59b65b3b9f891c826721f51a7ce14f94e2ef4cab868cf852ed9bfb17cd33ebdc4d688d6ba12c74aed2c8104d117574642af25e9bc91ba3fdabfdf1ed13c6e59fa1a0a4388925d2254195c666a6c235174a73125790bbdbed463617757fcc17cfd32da7c7f89e755f051aaee186665d9de393a82508d15f88db0271495c959bda6651a81a04dfc2103364f95f5464effde48f3b7ea80af9c1bbee99c9d39b0627984f09ccc6a4c888d121ca34e56760b1c3178e48607325676c484f4d666681f0d2454f7fdc59cca37136c5c06e746987e6a116728f8b0ae1ae9a32efdfcc956fefa55577ac670bc3d66016391bbb833d97f91d3a78696370a6f05efbd77cd50d4697c3df5699277eb1cafc1883776d9ecc72a763e5b65dbdd184efb9696cf58360e73f2d4db5e8bc6ad65bbbe7a8a994f5073fb4b12ad9b5196c450e27a53d8ca231f670966a238a6223b47d90990b1f6796f93eae971d4939d99dfea29d4ba2e3feffb97c0b27d6b6d2ee10751e75c658b44ee71abe9230f74a0d7f232329b165f5b2a3e584dfedaafa9ab477c0c516b6553c58797421f8d1e9236f992b61963c108752b1c43278a9b910926b396292fba67211e9a16b88aa9bab7b6b6db29f30ab5b7ef1568d46a010e8a27144a5e958022be5cf6bf4305f14e10781323acca3bb1d6b567d3fe5f652661dfc54d71ed9e3969289c29bff0c8972899993505cde2fca11c821d9280abfd5c20d4fb53380", + "slh-dsa-sha2-128s" : "0xee44a0de25b75a42e32080200c89b82e2f89551d44da272720c816fb144aa42ca29cc7a0045d97c82676d36286a9f1bf4d5b9e44b6855d90f25e03e3370cccc7665f3c7fdeb6f9565b76745a7652f70b7cb18989582559b81160fa32c7db7915f14c657ca37224955d174cc69de41774e4ac576f362306f3383733b322f8b333d9d0dee44d80e36a86491bfb7441a3c66809bc600399ac9788b4d7796604d4ec423c40dc1a6fb2b02db9a0d35072cb5aa617ef769006c504052596186ac0d504561c815617067f669b29757aed1de7a03c9c0410fb8b040dac268aa95232533eef1f69cc0100b33c9726d55af73b5d608b501b0470c4e9340b68251fc645bca481dc2d375de626580faa7b7faba8f8a596f795481cac23240854839a1100cd92a6410291ccd40cfdf1f70981c568b32904a0ad649de41b32299dd5a8bf50c35e5dbbd474c258946881b49e335e01c72ab6654741e657bcc5109fbda7297999cb4b272302739ee07cff0d1c9ef454a5dc5403cb7c170337336f94d56f6ccf563af44bcc8382de301720076a7ac1edc1ad95dba129760d7e9cd4df609bb8f7b389e7ab1e85e1b54fa5e3f2163afd7e24c5e5eb61a9e8e79dccefaed30f5fae83bdbd8a1673b36ce894891720b476652a26401e7890af9199bfc74fd0aeb6622fed8e488b8151a6c51a11bd808702f945ae9f9a016bdd94f45279d4765bb376df7fed5506b52b74c867c087c3386f74b130cacd7bcb4e832b697b50c796fa8fe4881c3f994820799417885af464f4f1a85989e5e3b54c3bd878a7ab054bd08c1ad850c96f3c0105380323908fdc94771609b10e489a8445f751f75a9b617cb1aec4460be2b53920e52f7d85096065fecbc6f19daae458f657dfc894758f3d9a6519fb4276e16b38aaeebcefba89a7686546901c57a29b95266d7ab87155a854ba9b269ac94a279e9c4bb38e6a4928cf6fdc3984b4363cc44e0e3cd7a7edf08388b9ecd5a04c55ac26b98f98cda79cd3f402caba28360899cca559be68d7a825022f9010e65f9a6564fcd0ec1cc4c0f4029086c90288fa83656cf2ca2cf30d5c423342cd15b9197905e9ab30a9d147aa2599abae71070e40058f0787ae39f39d6e5c1a8184b2cb3b1e6e45c40c61d46af8490cc9af6422a439f16adfe93a3cbe88f63df5eb8fcde9ea111ca96ea7be416d030500f2c7cf967dcaa4d3ed76a613b2bcc034ed3aa84f35556e7a65a8798825b576236798318034204f041dc59a9f9c407d34c2ca2d77470700ed6442cea5282ee99ceb71c6bf074423dc0785a39b89b062f89231af868812777536d0b144212d5b4b6fc7b87ea04fcd6f94438704f985b746a13dd519c57260f87150db4dc483b664944b9e805197df797be099940fed468b1e214ae99a68e99c6b5cf3d30fdbb1b3cc009ea58958eb4c4a7b7bad6b34fcea2a87eeafda01dca5223458affe54422ca70ef541ca8437171943d5bcdb70eca8de02b93098653160cae52f4d72fe5a6a7ad51c6465388f0362e6e72a3c96b9cad2248e8d9a2cdf89747444c7e51e2f89eb19daac76da37180e4133e0dcab15fc72c7bb90c1024d03eb2e630fc6d2537a101961b388e32ca5019a626b5a23914b06e7822905a5e3c21369b69fa63fc9f43cf7ecb6749c668c7a940b82f88c71802ce6e3c68b5c10c9249ba356bb3bb0539b581a8bd6ca141e5dc2b5932251746c1fc1c3da475581f3e3717f5d905f5ff3c83f1523f57cb750dabae0404beb55aba1105b995268906131835718cd43240bae9aecb30c794a8b186436cf752179b1de65b2a87917147612e3c95288a3e816238b3fccccae1da8041b4f784ddcb3d37ef3f019ef997bd8a4e172d397106a5adebb166c5f565016bb1c5f7f1a0cbb5bb22cd642fa4390ea640c04a968fb5a49ef929df1cafbb69c124ea58b31bf066be388d7b6d4c52409581da785d6447fd72b54949e4c1ad08fa64b520f2b8df83bbfa417952b87fbf802652a65df0d388ea32dfcab3227f645b6b28b66087819b4339ad171a48760e2bf90f1a01184389e23e7df583eab7c2dce385bb7a4d60ae5573966f916bb84aaec6cafe710f839b103532f9a7a556bfa116a05480386ddefa2858ac7ebdb58664a0e633a53e1685d71d5aad11f5eeb3d9ae30ab120fb88f96f383bb86917b72e02768f071ffca7e3fc2ac79846a36f6bd72c58aff03f9e87fdbeb65c8ce5976255227778cfa40ce0eee943b66a382e172abc814ebbcb0bf49efa75f87ef8621d227224b7be51b73a928c23a8937570e1a046bf1fd4c6993a9c14e552915953776265f78882ac8302f70900f9d612d2e7b7dad88cdedcdce7e615baf89ef249d6d535f92a2ec90a7435785f4d0c7a75812b875ec22e638f4af1705cc362ecf52c67900c888d068dc6f72a161013c30dcaa49cbf25c56e8937aa4897451061dcc7579ca99a25348e76ff121983444553419e820a3d1a41744f4227dca054c454a6497e0762155e2a06d17acb44ea55f54e4d6d2e1631a63d9494cf69c38ba66deb9d95c0ee01950dff8e6a5d1c46fececc7027997b8ec626f8405ae99ac20600898fd7fc422e6cd3f774b4009f371ce38e1f089ec93ec0a48d6bfb90ee79c3bcff321eeea33a44022ab910458984b75c5089cec0d8beec20c4c39311b991154fac7c0461b7d667488dc9f0f5497cf66166782e2dfb69a71031fd0251918e4c1243aa4dfb9fc2b6123b4f02f436c0f8737a6663af2b99fbd22c6af539ca9389bfb4e55da2c9ad42521ca6978f71991f53fc8cb227e228a62f3e22d7c3eca8ecee260e55084ad3ace0d6379d5eaf82bf6d3b69c4e74fade4210114d95b41a4c004858119a9a9b178d5a0cc97386bfed0dc6edbac5281c2842951a0c4d7da10207523973c676b8e6887a087c1c2f156970cc6db9cbd3c8ec1c9c1bec41fcbfdb4e8929b67e6c1939bb990ee2cfec3197b4ce824d25a7b4c765945e85ff2b096037adc4dd763b87850fb39aa98f1d395c9353ae3b5b53842a7056c011fb690e856401fc6f67b1c0a5414fc229284e74c230ced260d4fafc5fad153a5154d35c3bf9c559239ce18ca9810a1254a0a6c2960a53d838431447739261dc3b055a0b115914af263ac9fce2b512884620451156c40cd84b7bb2d449aad243a8aea3859412476ddbb5eb73028bfc6b2a942839e363262f94795aa45f6579680997d6acd7a8c8daeace08362fcd420f2f8a96801c002361b942493cc5ae79884eddee989af9832d55319ec033956660c5f11b9fbff9f1410a18c0e9a38e9261bdd13bc392a18a3df97aa33e7f59c125f88d78c86058f3c7e9a9d3b37bdb1e05d90375de0090b418de3ca761560f079a6ddf328e72cd89dcb63c9dd0357ec736782696241e2095aa6107a8bf7bbcde26d5d14533f28ddad7fe1b3f0c181c0e6d55204f6ce79cfc557cfb2d54cd2f33adf17a837243b51705bca7ad8cc452e5340732e678274f9eb532bfea122cfd2959b2499ad25a18db7dc387e976f8b5510821cfff778be799b80505cf38ab1ba6b01561ff81ef17f7735a1ed6a319a7f571daf92afb7490fc17064ccb6a8453ae6e66a2afdcded0c7359461278c49d342df84c43a0607b7327853db675e4c9ca95c52ae013461ca0763d157ac992e219a6683a547c32374631b53138676f50ae0d30588fb25de808e1d9efd5fb9485192428477c2f64fcaa862761c7f5505cc650910224400e2a2db13fbea0b0cc358d163c6d4a8e6296e4c9554078a55f23e60de9bea12ac199cd19b97babe61b2a3b853860b4441ae8dcd85d59695393816c64bfc074e945452080e15bcf6776cbb6b251c13be63932702468679b2bd7e1693a1d910e1b62be91e177a8dec8e4d7642e88e513e3a871ca502854ab0fb7fb8a099d0e0dfd73064012051af337abc78bbbc6fa2dc7169ebfde2d40167048d78f12cb5e7c130a2137a0b26922553f43aefdcdcec55bdbee0fa752a4787ea9d1c75d1bcd9d28135515a0ecfa9735901a0eb89f915d036e21263f90606f65e62f6e5c8237db14d570612611b3ceb4d026ac13f473e5706cd7975c95d49734253757f9c1e6aca9261cd2b95722098c5f7c74daa6c926ea4867ab54e4605a15bbf7ab94348d9b45c31a1fbdc7f420cccce25abb4249cf413008a01e53296318414dfac822cdc0bfc4298273f65cda3fcd61236e255dedd879fbf20b8c4843e27ef413c7a1ce2c714c88358091abccd7fed337a7d4f725ffbf7856ea4694113d605b1ff7b2bdbaf60f7abf7d43eebb1e82f4b6b7f3f8fc23ae27b09f78a7a3b324c7790431c800293e08fed2fa49aafe8f5670d4e6bff4cf6e7924993d6e18ad87699da99a2f2ca9f3c7b44b3b61dcfa5f041f7a6df94131b8e5c04069678541c87872daeb2943902afa6ff4798e45a75659568a02471afde44f8e116fff4eb7c16053a7aa370bcddbf5229c231ef8f2e642c1a88f90e0a309d24a3ab28d3adc176665e522cf5bda0285baca5bf2a0fb4c0fefff3b8ea1522c9ef29342ea57eb4f16d69bcf948d66247ed2f2cc4e567476f736d170c98398ac7c0d158f294ff33ec8d2794ec1929c57ee6d7f46da8e40337a2b380717227ed82eb13a14d129069e98cdd65bc34472fe1a52dfa7e466e559705c53d73d97239e38bbd600aaab107ced36fe7d8ef2387a05d8548a48a684fdf02b984022aeb28e8c5eccd1941ae19d2cb12a31de296b4fd1409266c43118848962e9e59a41054a52bf2f20b09eb4e77cca72df70cdc4d712d5c0313ddf9ec8df2d901145341b3ccfcbed77288ce3e4afc677ad8681c746eca02e7f7538d0108b68a922a21d6a8c2cfd67d8849ff5779de1c00588133309d4158d1738e508a1a2ba3351a46fd3f86339d66b1244bc2788de3fc60144d03c08b0ef08914088f9ce947d2b230ffb9ce176a4bd45b0c8b100e17ec7829eb1ce10b1a514b5e33d4f40792f0a300a4c3ba7a912db8e19cb72ee02ae8ddafc2caf116df905640d0cb65cd0c61d38e0a9741d3dc2b7677e8ec051d329183c954ff9c0c2d8771182d5550a742ba8147ec6571bde519c2f57f10aeb1c8900f8324f017f3b1dad92fb0d19a76de75c0c7cb67d079c8bc0bd7355fa25752d5d00ab00f9ab332aa23aaf8b5cfa4d2723b2020c9e494208c61a76e18c9a583172d6ddf544a327c931e8b908830e46f184998fc0d34ebe767e4f5d06f99804759c7e2b76a8baa302d6950f90194b0cc694a0d351374cd028ba6b0414377a850ca2f209d13cb7daf43d14f606a0e549ce14189023fc7b45e2b095a3f45930a72296eced3e2da24bf237959c0b966bb88a3a9818932b1cd41e8ee002df515ec94901902b2e67545dae53e08810b99dade7b579b847a6570211687a62a56dcb324bb442c4fc22f9d85e54b3f939937daba84c86fcc36c83ca87de70fc7f4afed335e8f9aae027e854e3a022a053c3b22be3617240f1355be9af3f4c8e9481005cbef07bf1380ddc012412c31b221275b56c3ad52cb2f7a9b340526e6128416025d4fe497f2d502070653d36f924420c0844b5560a8728e4a3be8b1ce67a1541c00e97aef9c808bd77af5aa280690d98f5ec218bf5c9d135d85069405b82c221d834b8285402f481b7d51ecdca91c4c2b27c12f486d382e0c14ec23c2bdf33ea8ff693af551fbe55e46bd84f335a68646bd092f914da4954c57201d7e812f4974a553f043ace9c6ce2c609cb4d90dc1ceacd5d610cf4723024538cfedc1bb22c9663ac541f89f97b37d2e1b4d8073ada1596e684b94e74c2c2f0087251f1eaa6789ece436b2940d3285f297cb7482aa9b8c47118a0b1d2031fc311689828b64855bc17306529e4b0a3326438b42698dd4e2e0c2f624aa510a2363583cbac47fbf9974925d81af9534d0ccb41e2b445eb19569d703cd8ace661139fa344a5e097290c233114370831d4710676f7e93889ed533454928333655b06ac5ae67f301f2b7944d769892ed76374a1f3861c8624b2a302df1a2f1a60d41e61379ab3681d8eee152c251e1b7b0806aab2f287338b752918cb989d864e9acaa92944f5194f8eba19a18a025157d2c3988163baf8b7ca9060edc2227ade8e05348d86d18f9eb633e4f06ee346e4c2912ed1c763a75fd1baf32cffd0c856ff9b44f1470136c0a8ae0b88175a7e7f993d8469cc8ac3f643aa7db36992cc97011be0a42ff1df8aa7cd82e260e05fdd67304644335b386c7825858a438c51b63f877af005f7d95202b54a2130a1f652ff7addfb798daf4415572376fdd910cd099066e8d2fe251a907e41bb950e960fe5116f4a4fc6037273250569b41ac07002ecf3b4b63f8b853f9693badd2e8bff695266095f4b2f38ee4d60428a45e7586d9d47c01c12c5e18d1ca9afedca6f03384567ddc730fb8fed27516eb7c54135cc7447af831f0c37727997ab2b01fdbc5e12c112aa987e95cf5aaaf44be14bf4882f49cd84894d555655d17aff21f077b2cca1ff0922afbcbf4781145cfb87df585e6af1955db04843a763fa735d33b3df836585c5894e5125d500673d5e4d0bdf6708d8d28c80298a9bb47c7430ae8ec52285a4c14371b3f8ac1e21306b34bcb0c1e3aa7a8dc8955cd9cac32a0f69d0fa0aab30abcbeb2c21d655ccdbae1c2040faedfc7a5ef29cd5d9227908335ebd257ae8c410b1916b9a5bf0e72902c2bcf701a18c1fe13695a8435b0d0129f3db883f428acea8b7f274a629865f1849d4c263a1536421875ffacf058f0128f0d012fd60f2b3fa6b71fbb96f3e1e33faa09b1d20b0919018e91a6bd9595e9c16d4fa6665c4d0adbd77a59cbbbd587029a4f306c887277a7a27748a6f289edfa16c7e4042fc81355b85a994cc6784f5fde054f188b9775836594abd82c1309a66db4388ad49f60794d8c8e1fe279f8965312a1e23c2956427bd6e289e55ec728f66c36526d40aed7c44e5352fa3e2b5d27bdc1f7b3bfc4f095d002bc8e7b2df2032daea9bd3e0bed2733e7ba13c6d8b45c492becabbef0e8c62c257480faaf6aefede888ce1546046f04ce7e06936cbda44c5f33c71c07b53fb3ac7a72c3770838471aace69f7ea454d2980fb5462173aaade7964b888606741270f92bc18950284d013b692bad63ec3726d8f6596788a032eee6eba08eecc836202b06ca43fcdb062e65f8ec91f7400963bbb8094de4b4e1042e040e81130a3ff037e1b7e947b0c3e8b7ab5b539f8570ec2cf08971a1bf0d4f3e8311b5c625b7d032fe2e0603ac1a4824432b4c7bb5afa190afa30c36f168d196fc89015aff8229fd4b299206e8183897229e95ba06324edc1a9ac81aad786d5c9d38c87c48fc6159934a8532489f06e1aa314a057d91c0f7218861ec241dfccce63d60bfa624652795229fff21cf5ca61ce05ea0ad6a1763173675377d1f2427564e397683428fbcc4b97495735d075f84b289b73389a1fece959f9d9b4932f4d33a0907202c650ccf65327d24cc24922d0d675fb59621e2bcd2eb31ed1ec5fddc96593d4f936ad42731a50f164e404b93920283bf9fa43f8be5878d18e07cb8813914508532625f71d02a615bf47018e3b3efed72d17e0c974d33df18ae70e3a4a743ac3c955d1c366bc94d789c90a8b3771969210c7f0c4b9b12f9fedca5d2b71b6c04e7024a0a5e3d0d3c7d30cf568a659cc17309b223399b39ee5efd0cdec3a34b0735df8d7e0bafa40a8aefd742b3e0157fc89d113af5fcb0cd136a9cfb82acce406054aeda30b83ade59cb6771c366c86b5d9b016a74f220a967b83254db3e24d3e1f5d5e23b21354420dfca6bd8454575c6b0e528744f44ff8e1b9679b084d083b9ebeff09df273a476332dfa172cb98c79ecbf3ae4a722c25be71a3b206d848f56b02fd04bd32ff959e6931bf6cba042f9fde027d832632cc40d4320166f9644296d2dcc845491ed780cb609c0432408d08dddc70fa2d52884df47a55a8a3e706a24ac6ceb375a894636e1ac121040561ef486ffeebed3c5336d123dc05a5cf221b9e895f6d3ffb24b3674039d50fec114d3648d6dcf4fbc0b503658672d675c4ac64dac4db4615d011e82bcabd9573fe9e2af224103f99c4e6e5acd2196f1eb2260fb4aa61c298f99c20c4ac1c9e70a9a9e28db441c3adf2f2b3a00ca3f1398b5a112126fc3f0e2d11d40db36d0aa47d59cf8ece36df8daa854509e546295bebf92465f32a7edae6af4218b49781034329c22ce9e87258216b4a901b823a227e46f2938df336d3fe03fdbff4efc7b8ff8142b4bb60592abe508675d04081e5eee99468971fd49989eddf4648d3948266b08133b8c22ce3523757821a05f254cf5691848b1532655e85fece5eb186e87fea61936fa63aa047b5cb77c08aeeb2991c26619139b951ba4c2ed487703a6123c9c16d7b6cd3a17ad108fbb7c79fc3706f654be77a6cd8a5213d3976b1a4527144e788b712f37cbfd5f4ea5aa5fcc0f91b98d74ddcacb83fb33e039e1b88811a2138743ffc0dc5eae46c688f48c0be92a7094f92c2d4bae0c1dd95d40ffa113f27b8fd1808178be0c3bf77b677df2a17c9443fb6023c9e43c48614fd7d09a904e7a064125f8b7671fbb7529169138feed4bf3e6c215947efbf44fdb5eb1e3a5700fc8bcd1cf2a393ed438e68b607a1061e6cbd46a0b4f30fb4a492e786c5cc6d4f80542d64d0d271308ba072deaf7b1ff1c5200a2bb24c68aa2a5cd07052ee5760e0ede484e0a9c482cc74ab564654787e1698bef1386fe446106af25cf2fd950a78f4b521d547c83b6e4b6ef652444af342e90bb5761ea0dbf6b8c315996ed3d3a1a9d1e5f2b75ab18d0bcc87ee639299a248406d9deefecbb6883fe81ad12f20d80fc9b3c8962cd84e5da215b96bd457d5146aeb36d08185b2ee021fef7fdd43d0555c0fd16e7b66d1f534ae0b87f6299fc3b8e70236f39c91601564a5c2b3abce248ecd5450096e65df8b69de8b5cd963127f207f0df538f990bf29ff0c2930ab8b378b86d2619b150ab4dde82469e40e9e6e503182882b03442f92cf7449fc2c4b2f7ab91cf5ec57b8b0ebc18b27ad9125da57b6c139edbe7495defc73d68e71019b2b808a1ecb89fde58ed9dfce8b17e072170983e3dc3551149108582b95b2e4bd9552faff0bc7fd13dc0130f62a967761928dd62c9cae970ee447842a15baac31a280b443390bfefb7d4115e8d1c6458e1920485985635b75bf84b6af5226dd9b36c94bcd1a5f3ed99fc3e5adc6f3c4a592d2eaecce02d0e5ae97e6c4c7e101d4138e526fe3f090123c6df8596505b4d28c1be9e0c3f83dd4ac07946617eb553f7688f6724c8d8d34281a8c094740170280cec80b61b07eadb2c63427690ce73233f18593537e07266a71523688e6fdd36943d015c1bde058ebf5fcd12ea00e6b41565d062722941ca346b5949078421cf1f32fd38ce1be52fd65f2e9862bc5c57b45f33581eb72d387f0c212d68f85baf7e42058e41966429b0189da4eb4e58c1104e59d34eb0678a1385c38289210ac86f77cf8bef670b9ed04414875a29c27b79bc97e7febc40c39cc8e201ca56b25cd8c96f1a37ff688fd5d55cf5232d216283169b51cb41c631ca323056f2e8f74d5178a83b3bf43cfbbbb40f2680f338a89b294d4b611b0f275cc60e99bfcf270cb761c1c2a6773503a3c0e03b2a858d0cb5f81b032012bb0e15add080cecf19bedadc4d0c7e9d81fa75b97e7ca5e1c9eec88bcd10afc0d8e48f83aee498633bb260d731e0f900a9b0ae3f0d6e57346b8d3d8019bf079b4f909b6dce5718052416de8b61e9ef317ecf2faec5a64c0ce4bd5aa72b79360ff9b58d4a5f3e0648000d90a947933b0fe71d81ebaa96cd4c54c5d42b941e73423efb1f6626767f0e6dde52cd8796b0db31cedaad75b4ff72b83e0e9ece164c4f4dac170cca65e42db8b7ead0fa6e2f0b2d9576c79d1d73aa4b7ce4b954958dea381a65d4287bb1e02b7b88b33b2f1872d92ec6bf79fbb726aa436434fc7f2e79f0ff8bf243e19c08f680a5012c3c3d0501abb46026c420e485166fff457412a075086266b854aeaf5ac3a300ff5501bfd7ae96ebe56a5df5b7abb5fe2d9bf7ffc0005b6aae9dca8380a66c8c14e884f9f3338346ef1daad62042d09cd774b43016b0339aa208c5d6c2f7373797202ffe680edce71d684030e5841edd57cacb0cd596c63515a1b992de113fd2c8330840915d3409f608c8d43601f09dddeda6c504352e4d8ed932d8b5076a557fa2d87914ccbb0fed61c1f4d9950be0900b2212a82abcc4d9f4466d1f5469939c1e35dcd2d124623e2e1aa2dbe90ce09b958edc4e60d7e7502ed16a8a1d08dfcef84eed8a93d6940441ad41e493ec09abce869a8de0a778fd8df55cd1d9d7f053a72314e786088abdf968a3b1d1427e52427c2313182f51b5556466960017468a35ed2594280ad8493a5d3503ee4c479fd4c8db0141fb6b77331b46fb228f94e213ad372c09ebfc8bbcc79eb074162b4694b7bfb8444a4938a80747d2c15879e28e3cf44a8ac2ef8c5d9f93e9cb42f81cf88035dab96b6c078bceb4fb9cf293dc964b0cc9b722a84f3423bd4fbc57ad1f68a8166e86da595fb3b7a4dd87190dc65756e0f7750b68e3f605def2821619fb4fada9cfd1c41343dd4f5424a1911ca709ca745962a484d981db73a23a85c046a3eb73cb48b085d3286738e689437cd4976cbd3e2c5a4a4e385b3aadd09b08859bcf5506164db33d414caee679c65069543f1db8b242dc424af5a45bcb5ef9b939ce94e4dfe56ca25702d7912c38dc44d94670d90109ab685ccd59988bb7e932ffebc882b7dab274065578c041591d481501797b4039da17684a7242622f4390544b05023d3902949d348a44d9ff421d04a55395cb4248c7cb0f13b697f1423be1342d31fa9b85aab70c16bc62eae5660256d2472dc908768ccb7ff02cc84ffc6059752499711338e41d37f8751d84eec47a51c7daaa273e19c523d3d97ee5f71571def6f7268f24afaae71eafc84986eb8a0bdd50fa553c815939a4b56380b22b650fbb989b525d2862c5d420c83509b5c76b1a9a2105b45c2182331ce72e8f1804efee93b1929338f" + } + }, + "1" : { + "id" : "0x690a7fd66157e5b777256ea5214f634f61b3a72d", + "keys" : { + "falcon-512" : "0x09a89834aba90aa88f605981578667395d730a408f3d112231997200c28865ec37a4a1f044c8817949cfc8a4fd9028832fc406f1bb919789f2099629c2c8d6bd128de4f51c02a73c49dbf17883ef90a9dbc0350aa67e024a893e555b4f989780a45320af4858421346445f66aa86e4954c0bce0debc896f21bc960e4fbd4eebed93e943a40387210ecda652c36b6da73570500adae1a2a03284dc47f1e370570a07007d8c5a64089e81e2c4d45eb6ca01530884070d46f4605ffe139457d5155c8e9ce8f201715ce7b9d18755304ddb6871551498985ab400cdc4349c6b5ed10b76b425e70376e39a6167622eb5d4b7cbd932aeeaca53b3e28ea06c44780b01684e66eed712559543cea677f1d9807e2ad0312c42e43d399302a0c4c506da7cba37000c3ecd10b58922889e5c8d31a9882ab8ae4b69d3d3d3abc6450881e9b79d9e2ac51085f61ddcfde73e57c1784a6a766ee2bc434d4973a9667c1c8e00eba0c544a36c593a764f519de83251ec48fb45a63e00ad4aaf01d1a295eda0eb95b27fc364c33e3482f4e5256cd622242840d0630194316033219c3051258f548a23c08d205a7b15199ba8d6507565d06a1f20db367183d56d9dcfd2e12c36bed83d239ddbda9efe8507451542942033282bce486f002a215c2ef599a6619247888a81782b005acb3d550988c1f22c6b186038bbfe1c583d165588e00c7f0f7e462a778e6a8126be71ee0dae7b5cfc6145aa6e65c8c136d34f7b45252652995898150cbf177f47ca8d16b583ce5b0e809567c08b99c06095c879b4d945b13c8a62429966347946b606dd78803a3c8113a431761dc868ba2e2541c66b39f30ee21b0ee3fba5eee922d56f49ea60c25a1094381908a69b351e325357d9b4b9e2d040d8be7795891ad46a334a057b0aebdb2e5a9cd760b02c774f60df240e05ed3708840b1663cb435adf9929c138b9fd15461d86328d5895d8778ada79d9b567386a6302b67b4b99a37f4362dea36fa2fa1f1ee2f4f8293b131c57595468aa5ba62a03dfadc9784c8190559887603c4b50b7487e795285705f07f90d8a9ca50e4abf0905d7487f04549132cb3a62ac2e490d6d5bfde20568290b693681218426fa0458eb356b198b41bc60505afdf7219ce5959124e4d1a56ba63345f91ab39afd970997895689c8397c023b1c8b774f92f0e5940c764abe3bd609a0d50a98dfe291e1677e6232896ccdea3d047f35c558daa130ff53cc66aa03ca5687a2006cb7c8c3f", + "slh-dsa-sha2-128s" : "0x3345627b00d77c13ce3ebdd12964c45d0bec4dcaea60b64fbf6cbe7b897b8136" + }, + "pop" : { + "falcon-512" : "0x394f68dbc8384822df8d4fd86c22e55256ce421178ceafde6e1a5f6437900ed2a86a72cd648af5e2ec15c79027aaf194847efd5855472b428c647a7db9d6e97dd15f70c3992480431ac6919e5b11c9154ea5dd9ac630f866c589a54b84a4c9fc57b86f2b5c835d3f6fac9de77d1f08abd507f514df6e7ba1c33518d3d1e447638eecc8a4b7cea388c2a57a6640fc599e73aedfb4ce6b3f147591b9bdd10fc7539fae4b2931eec267fbe70a5c6dd8b24bc98276db5d8da487bc38298d17aa7fa5facef7cddfdfd6eecbaf7e8086cab1df3c8f3be1913d30892aea7bd03d7ec1296247593835d8a74f467806259280a4e652f7e1bce11aaed31fbe2e155964f7c37243a59dad5c8d19c3c6b9d0f62d59ac39578dacf29d9ac2c0d5f160f7bece5902417ea82a0736bed8a4d078599134506a7b0f8a3ea8d6749fce0d749722f7c5198c340f53af2b4e1054fd97239109aea654bd2be0307a2d868570fc34f56a95ef5ecca1995681cbfd52a0e309e9db779ae71aa921796ba86a2aabdf6a39250bc71cdf8ad4d94fd6a758f29c90a7dc44839761f4a1a9c6d1905dd18d0532832867e48e91156ca67f2e2731bcc949d8140a850a8aaf9dfeeb9aa339b453bc906fe07f0a3e30f59a941d9f4f7b723a639e5e10f39cacce38936fa58e36746b0cbdece2a9122a110099104543f066515aa7750ae0080462d9254f58b5064a55b23a06dbff5875966598a7ef2417a8f32231c630c7b3d574584c2344e2fb38d8bd4d9437aecd68dc690b8f1e93f0109ee3747bae997419d1255a5c14fb1ed17b112ace324a41dc34a5ab81630fdb178641536da5268d928d19daa49507a5c81aca3e061b4b5c490310b13292385bb341b0550cfff7d8c72de94a6462defd1e6596b351ef32ca474b61c951ca5bc4f919e4", + "slh-dsa-sha2-128s" : "0x6e16547734391aae71d29745c3ac8cdfbc98f07ad9a8470650f0a0085883ff970643e21b67d6ffe83653a99f2198f821cc080dd7f0cd1a612ab5c6f18d9903ecce34a5cdf069ac66483448219c06b131b377900b999f9aecf637713feedd8c7d4beb03ee11bb297bf4a97959ac5c85c0c83b48147fa9a3be7543ce1050394e83ed745f9ec0c555328d3786da46acac3074c0611667ce5bb4613436dac210c2e9cb186e9540d7d43ceed7f278363811083368d50f6d258a46a32c8df5d62c42c4f6f34c627076169428e646774dd6dfe6924e2240e720fd4d53dee8f880c782862de1ff96fc5204a17625ca008781cb120f74caa37a36dd20136567c05e8e7723a2e21569f065d4fc631811ed0b2d9bb67d2c82b755d14cdeaccb797f207427d572419360af1c443fa68064a653b1e21a2b83fbd93e38c7cfa5046eda9b86c68f3855c61cee9f799ba02e611a1b7df6a15fdc4b4b09e51b3b5e1101d0dc90a213cfa0522e93808fc4ab3d8cef74d279b98069988f6b8c6b4c8d3d62fe2a5a62f1a7fb0fbac7fb36529dd85416a330f35a7e4e161757a83282ad50ddf92c6096cc1fe071952dae4540e0c9d0507d4b61ad34751ce17fdf2e99c3e54cb523d3ffe2cbe8a5c58b84bdde119175e9599df6b2666a7721e246305457cdb5ff5ab4ec9d04bca439d59989253bbfd9a91cb10a9fac3134d85ef28f1e4d4f884aa195216a4811be7617c0d6004c7833088851fb7758aa5aac838ddc5e3a51d122fbb03b0b5d0a3593b2b97eb69c2266eb236bcf3134414536ff36895bb081e9fdd2e5a9f9ba71310af0fa126eddac07ca67020ddd4caafc9b102afd4d0bcae2a957bd65d0a15762b98f4dc7b548cfb463ae10ec3da3b5a4e9976b8f05405904aff74d3d536f9a321b41e0c1d5e4fdbbf61b42d868602e112b53552b8199a6618723e5638273822fe0afc5e6fe068d117bd5af3a1c1e8bb4ea9ae639674b51a523d19f950629a76e9a52c673dd153915863d0ad166d357f8d23b85010aa0b8263de0f253a0ec29b0973af15e4419d965164b5fc6a0e782716d86bfeedfabf4d9705e7093373819a814a45b245cdd13a667b31f1724b5cbffe3b5ed3087118715a0ee68af333bf26f1ac1ff13eaad6a5edd5e760e89518e24a982af4f44386c6d92db7a2229bc9c4df23274cb0f10d348f2373a79f55ee05e3dcb256df464c5f3e54ab790f1fdc585cbacd93c775e50bd02f451b69972728bdce813b46742ebe9e45d45c8bc23d4b298b5956e03b48c0a11c602ccdebace8f92e2521cb346f291717cc1881b0fe3a2e23d4ac694b7f1cd296b32b7f2f139877a129079a524d7e3c4f9c36b5836346a6944c49967796d8564ac8d2ed5e804a14c655ef235fcac5a8cf0372f7df952af0de7e4319089b54ea3f718b539a9bca631ea0bcc9259c64626d252245d541d90179364072b081d70da0a7e561ba2902e8ce864a398dccf55a4f583df7b6f6ad6738813eef6815f8f67936b7b842ed7dcc48808156d236d70a9ecba41994bc0296c3a236f2772c747db6ad62723c4457bf8a2e526f306d8012e04adcff0015a88167d199029ecbd333a6ea35f5b9dc2eaf11a14fd09b35bb2842a4e610da4afef15f332d5d1e82c7b0b452b7564eb45193af060595975d496ee59151e3c0f1545ef2694eeb1a2e38d4e008c0943c8e7f181a258c35e5068df39376aad6ef356e0010303d9a0dbcab91dc708b8e9482f50aa18f88f45864d539dc0818148149aa259eeb270a474b0a117de91b6834672a5cb4b5fb7c2ea0ea7e3bc9884163887986aff1cd89c49a6ca417dff067da528dd33dcd1f7db96ed50820bd72dc41984ed5c767478b79d8f61cb7b417893a3a4451129525a915b1e999086e6db74ef627534015206953ed3a5e2bbc5c629b00f3f3b22a37519426c20b2a10c60087ea32fe606d81c6457a02bdcadd8d56360dbd278ced2d4cb9af64af4ec77989475882f3c1c0b9f9e0b6528b9834e34024dbbd64dc19fab35f3bfda66c086f4c0ff06a29e9fe881340d1c0719ef568e3f8f683282b963005e93620c01de327adc0af4ea7f968fa987cb8f38350ebbb3b91192ebb009f9fa85657e06cca6f71edd3d0dbcfafcc11b908cfc38a08ba288910fcb14f97746ac20ff6e6c43eb099d09a717ac03f5e0600f0bb611d6972626999f5661e048476aa999c83e3f476e77e7ae09c6fb7c343a196e1846ef061be75fd8b662932fdd2c7d8784c6e498d4d828c910eca00ac199957fb44d572d4bd2c599fa6c7c2a166d96aaf4959c561d1e9dacf502f76b050c836194fe1897d54a017944a973f49b2ea59b66c24d18f13048e093e83af249854da9fd6b4974b4db3e8e65684b3b6a0a11d942ad5bb667460dd98ab54319cc2ab704a7a57c1583b8289477f5557d2da684aecf66eec3a54da742e2d24a6935ffc4f439d4a8abc21921087df93da2bbdc3a0f61729b397faf12bfe59c85a6af75c9f4ce4af19239840b57c3a4ead6096f2910da23bdfd02ee4f3676897f475d625d0e9a9cc930116bb2327232dacbd0ff32cfaa40aed89024b9ccfacdb76bae77a87b569582442ad2230c8fb3e0dca3937dea5685c46aff9ae6e16438e0e9ea8ba299936a42d68967926edeb3ef955d95c5a0b74a13edd280bdcb462dfc2d82e7ec89377edf6760450e1ebc61f035fff661173de5fc80dc66b8e3d38c67f4ef8c25c2c5626dd2875e10d870f12b3f36f749eaa0c82e94b2f30686cda1da606e0c815a35fe6fd671915483e1b2b48fab000ae560dca0119be7a0a3dfb347ef95c7fcd9a78e76911cb5bd438a7e96562e437e32eb399782a9498ac992716bd9f3d7d4e35a1f1821f9008f547d171ce2b69b007b5e377a3244092396acf958932616bc5cb95059762f48468a94f9f897736b11bec2ce0af9ebaed9add43bf5c262106a96083ad967b7fcb127012b1b37970a6be14cd9088f6ea927a66a76c35e1320452848bf7496fadbc613b2e122e55634f0491397ee5d211d780971998244f303802d7630837d05f3dcc588d8ca32e02f16d2297aa577576ea5c1b90d6d330a18444dba772d7aa94d0bf2000d598a069d03e7873f114cab9174af2f28e257ef2c0e44925da7ccb593d6183fdd37a1abfd7107ce02c655e7966286b72d65394f312d72505803bd53621a08100a041210cb912d5aef260b84e39faa7b5d8a539b6392b32ce6287e1a619804677b3f69ac46fbec010e726f3eb3458433e68e6417ee375dc70c2b2ee16b2cd9b228d6a46f92ba66a8c5b08cbd284dd759478c9f690c5e1e16d6332b5a0b9aeee43ac8ce3eb0c3eb6940b61a65bf7c3348d8dd15302b8b7bf6b6257e074d0af3eda088f7c22e6778c8cfef1f15d9f86c76a4b05c9afa228bc4f116ee8adb959b4ed9afb2307f2252b10baa436795028629d05277ea2722ffc14d9a5fd56e6cd6f0ba218675df025e50586e46429f1c6028e305d22b5479dd045d98a93fa30de08bec1576ea692bfe6b657b4be65578fa2dfcc197212643ec9a1a8de5971c07023abd8767c528025281f9854145e3d9c47e01bd37dc3a35b76639e4716465b862b705ebaf674a64313641de333828d75c16d4f6ebc4193fc2c92e5a3373fc79dcdd8505a8ea7ef8be6ffcecbb2e80017b6fe401e49193ab31e67a56e8163cce767abc393608e7485f41983ce27b9b1de1a0281f237d685d401641f860f8f11d10805fbf0c7125deaf583458787c6f2850b0cc8c62053e069df241192c6af9018158c27e30574a9025af5af73bd956bc008efe84b31fd9a49c110b803947fc939faeb773cd3c33d1b6a96fca0c079f2eeb4e3ff768bbe1cbada9ad22c2d97928389e5f7145a346931e4138d66319f0154cdf3b28cc9bc04139f92e421efb522d0639f02935e228d6eacf3ccf0356b32c163dff6b597d51b64a13c8c93f60f150f445271489db644665ed1208c025b35dcebe82e73034561704c602d1b24e04c08e4ca8fab21c5136b6d4b7af05cf8ed62961b4bc1c2883154116a0ca8933e36042eb98d851be695d8c4de2c2530372c606c93bab254d6b00e59fdaaec98c56415da26ea8449061e12bd22743a1a0e0e1e117a4a9448d75b3a9dfe6f213460ca639632d404af60c764d91405391137ab9ec4e8519d11e3dcae18dfad0404ce14ea4dd36dc3ee24b7a15709410818ed0a42091a01ed9b7f39f7c87d60d2b3dcbba880001a519fb7de152ec06c73d35c08b3e375b9186752931b6108a4b84189fe07a7165e18357a94962dc2761f4eff0df829427646d2210b55915be710e2a1bb413df2055e7aa0b0b9a4067d35881f22c08e61b587a59ef82ed4df267afbcd4c62f22435167f2d7a4a939eaf28a6f1903c8c046b2d7719b7134834cd56964ba860e60ebb6fbf4605a11b52aac2d595cf3b6533fe2452b65dfa0fff71aff39124a88e3e02977d6063a8370f98e3cb8c80f35260a8541b9584b1d2bcb315b394d7641ca56539b4ca8bd9e4fbc594b0a3bf1ed678be2f9a18d31d610b55ee175514738717d47e4821150a799ebce655d0c75228c2160aa1936dd42f9923de2ac97d0dc3ea062d41d5beffdd1d69672b73490d6a5e14a655228bc39ffc8f1e862fd39dc950dfe648e0ff87807e543d02c40411ddfc6c1f8eed733750557c9f68d830c83af737c9d8c3ff0d23987a8e09e6293d707940eef4cc838365775046af35dd26acfbb4264a9ab84818a71c5a13d8e615c5675656671e5fc2e91b682e91eca76ebfeb1c09334e962496f583affefc56dc144873c79d4df77da394845fa29ff348007c8ee7f23f2944c121ee3177879e6938eb043a0abb75991b3e789571e3eff009cf089acee93612e97be85e1cd83da9ff5006c14151afa67fddbd9262667f98df52dfded25f28b36fa32ec662cd5a6b0f844030dbe1c87e9e2d435fca1a81045b8de5b8ccc411a839064bfd133731f55ee2ed2ee727d9382d130ced8a48942ea295bcf75fc74e24204b808956bfc171392f18554764aa80e75a539941d90a4afbce1f981f0c35bcfc78330ecf3d416d0683c7682cbca066a10c43273a0f85495cb4916ec0eee9952431a11717cadc95c7626436953fdd5fbfbf9ffe329159d1bd79499807796d75c458c3e3914d1f73f656e9fa273244615710c3eee84553a7ca0250e97a0b8895695bf86408d2f3cb2fbffffd62c8583af5b25b7e2a38e266c9623429f8d80ab8fdda6774df598f966b86ac228639af66cd0e3ee7ad7714b4ecb42876c31eb2608fab0c6ee26e97fca305aceeee80bbe22cb5e9819322aedff05cdb78b1fb3060ef18ce0385ae0fe6803b414aaf0b340eef94a7517e5cc3df4bd273f92120b8ce73281cab96821ddaab08b41b97708dbc7d0edc49cb362759905ce8d42743f83a0d75878596c4c5ca878b7aae6fee6ca7b20f2ee60ea773499a781b958f4353ceb078e5c04b0fef3e0112dd0e3a7ef19e4c63a202fdcb8ea56d15b2fb0deee4198a19c9aa59c1ef2b3de73f291445c248779eb5fc76953137adce5dff20f2198cc9e729be4adf385e0f25f67f68c04b4cc26987c133b987f477d89977621b8313d6d18a5ff6a76c73ed608f5e4adb860e2e8a6cdf237d4871a98f0c448668bb42f29ccad560fc1e571f8abb3c75665a65a212fe9d2c1c48eae9b92bd0a8b7b3d2fef99ffaf7942f5834533a592423d2cd66aa793e85bc8351876946a1e49e13ee374609932ec1af8d4d3f52e00f783368aa2bba882cc91c5fc6eeb657f3682dca8e9d63f319df3bd1cf52c511f600a6c815e4ad59426d9e1f32831dc5d8b0673c7c58e8bc1601d241bcb76c1306ddd5aea847dc903f376bc4d8e73a55f0a5ad331f57bee70533b29e6b8493d9f11ce4f27f9605f693c05487bd43882ebe8a1090a95abcfc186d74a3708ce3351ce7226183a21c08497dcf52727b7de08ad6a970b2c9f314bc7b33995ffc77a9a36ab46c7a05f61315d18b3f2217703be5753b4ee1670eba3af00e90a94664039aac93e89dcaa59d4566bf286f33c740feb763d0a385855f4410052c52abcfc972298ce03ddcb2e65dca251afbbb75c516ba77001dcaad8470f61e3391c1c68b1b7dfac9e50d53d2ad12876ba96419b39d7f94fd88b58ac1c123790bca733b12dadec6922384c6f3b7284334bed918acc06ecac7674428d56496e9d2e8f0175b3c7e384218380b6d59dc93541ac0a7419eb0ced7ca8c12a487fef9ea50280b7f7067a8855e784362e85703f6b848ddcd4bd31293baecda021fcaa7198b27d37900ac192d71de4e5124089bc1c8234fc455fe71171fc44494395b323fe3575ac720af5cff428326cca56ceb0c47e20e1432018a769f467405f5a8b71e15e0bd32f027640212d893678ec6fa16e5c00bf5c0eaf2de0333126ebba6b2306211cbc731a6baa8fc5a64a30141a8bde237c217161e384af3355b4a253d35766a55cc69be97abead95dc2ccdb4d243f64aa6d74330cbb2fc9fffa79fb6b6baf99e5ba9abd7966efc1586e72bd25f6563fbe125abec67f24e3510bcaa34c49499811ad2425c25593ff362c90c5c4fb3c4c45a4c63c46cae8cb1759ff2d82ac2726b60b4f094b6344191f3828c78d369956b7a28f650f63260c2dcdfa66d1df4731866efa6a335281d1cf2ce3635eb4294c8b2e1d4b7ced7b4b58a10d693e9f5a269b9d3451198735697af3a7b4b630b835e3b56e252f71f9aa6dcdfddfae13668bbbe3538d5aca766dbab6655c4aa906e0106640d21c30f1a241f4f5dedd526e8f579848f1b64808afb54bd2737a18d9a0e4a078705847b59a1ca4b2b554d03304daedca50726cd23326a6507393b2dff27921b5d7d322cb9901e0efce9bfd95039bb3bf6a99282f9dfb1e90289e986a657b3aa212cec6e611423eb3e4523f4723302b1b4ea5db2cc3fe3a7f0f3add38eaf228a6f0c02b9237e4078b438c01a0856d64053427af9f84eb04f022fd12986287b48cde73554b014f75a04d01f7d3fc6285347be643ce214b72c911e6c5360909ec590ffec8b4395fe271e97af852edd3801ccf04d3ed6684c66c67278cf331ac7c9b748e2dff377cafc7fc6732352245aabc9f5f25cc29d8f7f211a88a7aac750e488ad0304b7907bb063e29fccb16dd7ca0b78fc3962046a83f9c7138d49df3b765df47829e5f56e5155dba194d5128648b92e4f5c11bdc9232725faea4b87d6d940b9bd7cead9462736c7ef605336c40c9a053a000ac390b6340dc85a6905d35dc83c2d13786d233952ca8777b5981b761376f4ce2c5bec0587c2b28aecf39371b8899d227dd58c054aea7257dcfd838c32d6053362842a0baee7f967b6a4f5e1cac240896a8ec8fabbc4a551a55e674dc8cede1b9cee3874ff9fd3b91db433a12e78904aa3ec84d57a0169f1719297b467c46b278f671f74b8bb3c2c0cc2a093cf93439c1267e3be38b12e9b5881c27ab47bb01397c6470b80174ede3df34ee51ac116bad6fba58f8b8e2c7e0d1dde184460a5bce9e1d853d11ac017001fccfd5b767afb69691844e8327d39660c06b8916e918f5587f5abcd0f0bc45328ac01b1ace66405ed6c7a5080f5795f2ba0c0d9e5d05f462d89aaa230f240fa47c5c0de0b2099e93ae8c0f94a45f0513ce2797cddccb5216bad88333f6fec5805e0b9474a18ff562e78dfe325c1a6bb3648b41595690cab5ca9805eaa8e13526d036f4233f8181aa8efc76e14031d343bd5d9023fd6cd6b32947e173807883b6a964d398d3fdf9f34a44bc5fe41c5d72ffe5fedaa13fa1da68e19656ed70771529cfe822fc694be1445c79a041eff9753cf47a48ebe9f2c9f1546117b97a1c2bd6e375b957fe38cfcbb29f8eb726f18bc6131b1e27316064980e35d95fd913583b9735c6bfb03ec86e935024e60679a61d972b4aa296189fbc268552acb6cd890c68e50e325719d2f2c99b8105bc92fb582eafc1d618b4e88070e6d8df4924a1780f63ee216c0e937073725353b0c97acae72ebe521fb05fa78a4d6ecb7ba2b860190b6a5c340c83249919c26466c9cf4a9b774fc823141f65196df32edddad59fb05ba24738d81734004c42a7f92f5234b7f4fc0d605b032b08eda4cdfa3b03a3b805ecf5e4ad667a8cf399efb3c1d6822d9d5bb5eece6e08a515cd75c4485537eed6cbd55efb0a3d393d2cdcf44846f0c27cf6a664c70dbd54fd56b20bcc482de2eba84163375b1bccd43da63467557828b791dfb0a976628206e26e9eb10f1e9b9fc34571cc1a38f54909a1ee0b097e0f22f3ca58b56c631708e9baa52d7eb551bd327a6e0014fe98f04d7b0544fadc06c16a5652ea16b6c92a014bdeb2c070843152847293a0a925cf3c22bc9ac23464919ba6340cf233990690c94601876bc30bd5283f97cfdee757caee7b7b41fc265fb2ca9751e3b523e8498d665151d1c67624a084571f6c29973e624703d2e87f1ae5a5e0dbeeb96f46698223e063d10d85fd13fa0deb3891359844ad5e8100036d9b70b3b7831ae78c927a5e2c2ec62992cc424894ccd6f064e9ac307e6a2dd092deed179cf60b4bdc6c56c32a538d477d7917f7203f898659104ad5aab3e520a9f21d9c01b581cc968a68a77648cd95f66b5a1eb1b9d60b0a73c2eb05873f279c4ce43409853df2c8db254fd5b63af900c764ad4a0aeb4641e1a4e4089967e83603cef0317d06d1b990354c11b17c83c2a78cb9152615b9eed33e20f61a83179748ed4044a65edf285b155d41c735a883a481fa07c4941865b86d3cbd54c345d7933e32daa90a1291211191e0a3a5015f0f60d2ff07782606c164485aa469bc97f74dbf1345ea631bacb972be00547074759fab2b98a78cc43b7e4fee587e40e23d9f8ecaf03337d47d27bce633ccd97c0b18509afe35d54b1761a4ecbff6c44dba5402f3111eb671183b5e6bdcd69398056b980c35628f03214e0cb4ee5364e51de27843ad6c24b1e972889d54ed2a31d74000c32fc90b3c96dc3761c9649305c5f5e75f5b5ab7eaee213dbdda1039d4f3f450c77e6434752b74a55c42c98719836fd91fceb27be449d65c89a823218e882b987277622ce62fd0b46130760f10be6b49ec57e25cf6584a99433cedb1ecb1ce25cbf5e2b6eb716c12064f2cc45f4989c4c1b6c1ebd390c44195a16da16340d3a8588754ae676a990df1eaf04336b3252a5177ca3b3b2afd8e7555e92be6f6d7e64bea52c8d25adce3c580b5731574729107afe0e1025e6da2f2b05d6cc223778ac7de1202566098d43d0e4db6df8d242b57353cbadc0711030c6612cb92a9f9685c88856a0739e61d08c8fc5864fb9d8f41b27b43152ec15b3307e2c6f20e00254984e6284763acc977d9e16ffd4ca8be3ba7a289e3c5863371c132b8300f59d50f9efd344d1cf51c5ac64f29210740be2dfb720397941e2ba939e31c9ed563a674628b6ec3c8983be01e41eca61f869b920bf08db3255cc04a1f459653a4a3c204e6ec5496af1984f5e47caf56993bce1124bbfdb5d7081895a96170241307f000ed22ba92d5f5d920ab39d3ad1e288abb76ec3acbda6a7779be4c65e99bc7d7d5aa57d08ca1774e0fb146cd3a894beac12715bf322febeb256518addf26df507db3c79cb2883e6bb1e4716e185026cd760c76038d52a2e0695d76b3bf9da7df9f4dea5323f7dddc8ea1a0031851928e354e703651f7a21df505a4e7f9f7a8a398582aaa9f504510c0bc6f9af8942601bb3a6df3faf2e97c9df28268e1dd466857d413fa0bab4ff017f12554dd317455d6c716f9449c5273ffc959f147d7e0da0c2eab432b83f96f7d2de7b1ebc41eaf2128f8eb8f9c09c7b3fb4e95bb3424b216232406b81abd4c0afe11835a8425304376b08a4442c6eb0e88a51bc67a49b143bb35416c9ce613538483c091bf181d322efe10b2204aa0f0e01507a7cb809cb54b5cca5f44c20da32b2b7ec52098e53de672795b68a44ba667bccac558210307ac27bc5c203a2e6a264e7cf3028cb4ed41e29bc46985c1440187155b8bd737fbf9480749d62481d805463d75ec17c7ba83c60cc7020fe4b110ab1a248f6fbfda3e5b8a688a280f87a53ea7154311951c2001a7894eee171a64cba9b19a21e09540dc5ba85b1b2880efc39c8886dee72e64f045d00d99a90ffc81fc957ee65b7bb93e113717e7e7a275814404198f9e9d33f45335908f1ae917e0322b3d12c3d298ee814d22f8a57304faa93c18a14fdf53f05499239b66498512a6856ea194705521857a1724bc2925ba34f0e2707c84ee234c3436b4d3ab46cab863a28a011236e564ddbab3d49baf77be49ff2f5f2ca75252971a421aa2d4975aeacedba6cd3629efef8b2322b516c60ecca2541112b59d73b88177cd3be06163a463306cac2c0c1b11f84ab9b2fb6ab776bf0dc0fcb381975e4c1a0198b41bf6c1587bf6ca7272d8c722a581bce4494be5431cc5e3038ec33d843b9049b018fffa03cb3ff58e5da6bd6d49ffa6f776a5c4fee3cb258c4bf92e5e4079e38437f321e0c29d608cf5f69129e705e66a7036d67d43fd4a2919271fe07052fd30c049b793d2af0dec2431ea687077cb7a7a5badc21fcb0c2ac10bb37247207596d33d1c2232fc2a225acb9f30be7fe6da19514def9b595929c1c0c4b146099bcd980356b601ed0701299ca1a5547d62badd0475bc76fe51d0565b95b52a801f14e1f1ca62526cf8931f0706e3a478d20ce7eb1b1088454beac15277a72c90dd95f6146f961e41caccb2cddb2cd0680760731bb6db221944eab186425b0c2caedd840be5a2575d8dfd5810c4db5403d4491304130bbc564cb578d814d952706570e56641e8ce6232a2d4b18941c35301cd5c29211237c274dfb1aae379e25efdf827ff6e41cade1d57ed229a6ba76bf98de6592e5ae3ffcd481b53071e452a0b25f12919003474bf4ad2f36138d3d7c23c89b5b41a25c3622bf1dab6762cfa86a5bdeb6f2baa9cb3ea7dfdf9bdc700f4062ce8fdf477a17088bb2bf654e2eb9ab943472dadd73e3b63a4fdcf90fffa98983130e16d512b54aa0d4acbb1604d00fb2adc4a88e87d466237ee7b0fb33e5f384656a8759143db4c377e2cd264259" + }, + "rotation" : { + "falcon-512" : "0x3981b03586d7884dfe64c33f5b7b388f166ace48916b895ced22b34d428da6cb1bf0b0f292968d77e45343982d586dc6c556f8ac4afea15682ff6aa72683367a22398495006bf5ac0e830f10559bca255d2149a0740e61f05322d4acfe3a86645817823db5c1524f27b71c99374eaaf72ac0f59033a05ca55c563d1d2b58ccff25c58e779666306e18c1a9a4bcfdd7b47d34f68a96f3c18ab227b263566951271db2b7d6e20d9febc759c0bff4189c59f6add4708f3d8e532d58660cbc12ff5ab24a6525d98e4ff50f2a2b14f041e5cd6f0b1c4be47ae745ab7a59e8a220e5fc390c92ea8b085334d6cdb33fdaf1d7cf3c477367f96a54c46f9aa448cc53ae521bd73dd8c6a652f69373c3c7fb18af3211b3969ae922a3ad33babe49c88116a97125ae74780644f6c2d31dfc5987f550e2ad8c798d4f0f5a05b356dae5a73949b876f37355793ae6b0c6729ea2d70b12c74c2884d76844faa866ac685de8c74f0bb2f5ee9027d731aa55b2e9b1ef5a13dd33656699333004392ca27769c6a91d5b26f3ac3d65d74f9219d938f2b02f5fab2079e2ebe905915224480b53e2a89beebe3ed153b1bf1f8e296b6128388a5625ad48640cac171c30e689dc6593eb24b8f575e09438162d4ed215083142fd8df2f1a67770ba392d92a11fba399348aec782c16af5a41168b17eb6104fe43b49a7dbc01a4766fb962cd20eab637aa3e420a6f9aec4a2f9697ca26ceca1718843fa445abf3910cd92d877cba165d6bbf86aa5536f5ebbe450e62b06f2a3a8cb03dc5c198f5c6d90f5337789924ea67fbb0a07c8a06ff158d72f86daecaf9c96f12bc0b30a5e39c7e926bbe241f0ab69930aec2ec3ade0746f55fe3b18c536132cff870ac4f9bd0efe7d84df72fa1b6cd5334e8e2324e18da66ba979091080", + "slh-dsa-sha2-128s" : "0x563b16e5f93863fb9479ecda8e51da4c5e0b8c2ab218e682251dbdc02341a70a0bc0a8bfcd7fe676b4a88ed00ab7bad29a5226e6bec830015cc98a88e82d8907e4b66e4b39fab840bc60df099fa85f9e079a4afc0fb91c1ff5f8bc18679ec741add6e6a35870278fba0fb69e912c6dd01dbab121ae9568e4fb8d2be66f506b4094ad1e4a249a01fd55d6956780c0252f978308118de5a33edf8b1de9ecd158c4c1be63cc25aab2b1137868c2cc9e5c5bda02cb50e7a5ac5cbd6c3500c755bfad4f65bd913f3e8faf204a3851410354c9beaf9ac2b69ac726a1b61a9bf625e9bba4d3ece8511f6f2aeadf08fca28d772bc1511a4f3b659b0a8f3c6c8376531540a9866870faa1b3306ad1a8d26022a08bc2a90a4dae1a3b67491357c619df90d0b25d7329fba316d7e845f1c324f99a3f4dd60a0f58ee0f2f0d28efc4642ec3374bff548d20d150c31bebf560805bb61b8638fb8940def864a2e38402257a0fb446cee1e38e000bae954fbf1f4f26db7631bf01089f0d81d4bdab306e142bd1e1f356de670679e7d0f3713216064b14bb0f85a56e9b90dfbf0b1c81aa4968a855fd9d60cdf4ad35090e2dbdf8de9224d9fe53902bc02abb46bb568a0ad2db5a73673746ad968780621277519e39053252c9e8fb46d44fdcbbc973875ef2e3c991eb5ee02671cd1580d2ec9e8c8035638d2f5409b0343173a071b2a72a237f8bd4ccb5d080fe6510ba2b355b9d39b3ca12ae06f2766d634457455d72d519bbaccb2efd04a142c7ede443d0db7b44c2d5cd0a3a468932a9a5370853bc475c622bb5c8c52eba04325d20a396ed36e5d4a59e1c00861f3d0b0e08a9f778c70c7e8ceb448ca4a52f6e0bef75b34a5471a3bd668d5d693f370048c93657adb1e5bd768b5b38f6efe11f315b3e6e62358c28894ad8ff475f98584ae32ca9c56b39f4a9f3aece99d8e3ce5d2443441e183e479964a18d53aa867d07ab6f6d9d019160421f283678a0e7774820005020b98fc058076fbe1c65d188c4d5bdea7cf0a5d67fc39820c80726e72837bbccbe1c396a79ee29c0c23ef9871795f25afefc81ba91aedc3d6799a093a0c0d533e094d5f1fa60c6ef72a4122ed34f3ee5ba6992b419ca1de2a9585bbbecb2a705386096e929e7bff99ba7a5e33805288c8ac315f1679e7f055d1c63d3229a206736ec1f7585653c0bcdcb2974d9bcf9fb805a89aa2ff4f57e98194ddf8bed97e4c985313b6487ff4a4131bf0a3e86837eaf9caac911548f3a60a3e1bf04d6720b1e4d60685b6db4ccca66123d23e17f2f004354bff43c4d61ceff52748c197296ae3c8c6380baba17d00531bea8f4c1becf6eb6e9fd50c86bdbade0f4da4015f486f970a812c778fe413841f0171743044d6e77789f1bafd6abd4234aff922148f3dba40f3c194053713b9b9a3d7047bb849cb5a806740d9424288bea002261fddc12a6123fbfc3c8dfa26753e92e2f6fd14e4f1895ec36d90f572198427f65e92a1ccf92bed4f56cfaddd747cb2a0a6237325eefd74da45cb52df13c7e78e2c60bdc332d118fe51e3f17cb85edb9d5070a8addaea082767ad40a9778e46a61803c733d1f27e36a4d1810d4cc77757509622ba4464ebc3a2f8bc9c9e49a650450f0ea57619c179e94eae7dba9c66cd079f160398a0ed40f685bf1a405752f9b2820525dbf697e5c198905f09106dfcbca5a5bc62a72b2dc8b85e991871a8e4d7091fb097e0ef13c63f59a939c02b94c58ed9ef9cba75db728d88c81619c86d95710c5b51733a84b9a8cf068869464c35d27bfb1daaf140d65afdee9b7ccdde62d5d4e2d83ce670dc49ade7b85d1d8167893d0c68faa29d159ef95d4381bdc6a4234ead47a472e7f9740db9e1d8c5a4c66a84907a056d625b6f8e192a89b10f6d4b655e74ca6b01e6fbb2b187ed75b0da052713862e7cb3457df4202bb8df95fe309b8eb87a711767afb1a3003ace4cb01cf3de67cf13e24e6d166e763a8962ee7f47d5ae9a82b951362ff226ec3bfe12b188ab659d8fbdc38e0ac6d0a8c68c5ea7a273cde1b53d5063c5c9bb6b80e803261ba27bd88330cbe619a580f2554171173f046ad43e2e5be710517889ef08cc9ba4018b68edf3e843ce09d0cb226a2f84ef49b60e0cf97b70dcb84a353adc79bf8dd7682bb18ace2282b0d78ab1854bc7294e3889c8eac041dc31ddf581e9697256428c469fe3d2c68d6ab627bc9e8e730632c518837a25d4e89aebe1cddb015b57a88ac7c3c6a2f62f2aed77ad8e19edfed15551941e87b560f05b24b3f261230ef1c3566b8cf121271611632e111756ce0346ad9ff9ce3c8caa1796641f5e019420273243dc1ca62721a62c7f182a53eecf09acbf08afc58907814f0ef90675f634b6c4e565a157bf4acacab2c01e6cf62f0d47eb3ab93ad79f2d1661b1aff34065e6d7e39746b3d94d92670c7c78e04f16b63891675667b83fe689d18b348d640cd5dc302c0b7a149eb562f29ef317218d5958763685bf2d86f756853538a5e699bb3f0e58c1934892cacb9854e25211ac4dc4fb6eeb2f59d99d2dae281f2592f0ab8f1f4e59708751da251342bdd5864e7a9b563e12c74e7ea762fa2db1bb5cac5c2b02b680f27f7372e7bb7914403f267c0e5a8a8e7f59b4f28fd0254b570c158917245d976bf85419d8778a3f03c02f99f853185882ad19d3aeb2382185ae6ce0349ff3ab4b7b419c6647b0edf74588365339b4c22c54bae1522c82318225c44aff5341b13a09df32eeed978da9069fe9795cda0763c78ff688c1c456a550ff450bc29df105407859dfb13655d8e6063eabcfb209a9af22f4dabeba0e151563ee2a20605573c3e5bce979c420928688be7c5ca0c66d6c4760299fd0699a7b96be126bd16c8b57d2b3da3a31e44ccb85626c1a5845926564305db1575137fa505389c17c736d648c96f1f529b100123d7535cfd9e6636d167083b1f2f6462ff227116ceae90240515329fdfd232762aee1d9bbd50fbae995cc4c27f34cfe21c924a603564bd3443ae0c2a6a78599705cda7d8c1224adfaf1cb0f1b7cbf6b2f8e1d3e69e200947a8cbac5704613c041b9eec1a947a5a45a855651275c63bf96d0bc26c964cd283e5cc2e81f7b13db18d58fe5c31da90bcd5e03ccbd9b7cf4af67897e031a73f7661289907c57455bc2e77237d6959328445b2c666bfaa5073914220324c8ae9043548f2cb28167d6d22d919f6ef05c0b71704fbf0e6ef338290b043319013eba466edb620c14c27ec65c853d0f07e55d56e12295e7e0b2c7d0cd342a428580645511780193dcced94a2dadfaa90e789775f42a5220f13a7757b966219da9ebddbafa76281fb4e4f10fad3b2c59bc1279e7892cba7c78f0492b683d6ad565ce4cbcc8678cd7cc8db72b2111bfc5881042c1eecf57ecace2a45e83dd10b60deec32830a8edba51a31bd72abaa26e47a59d7db74612a52544f05e3989d93f0c25a3a3802d86f025afaad0c8f9383b02bbd671d5b308f7416cfd3caaabc7875241e5f13643022e2e80eada5895a48d2d4fb5dc190f197837463b6ea5d8cda125dce4e9a1c20dbdaeafd6d5c14220b49ecb149848c9714d41e9324226e6daff80949fc51cd0eda6290a75d757200fd1832fe20a3fcac026cc04bc75f91310e6959d417a1370cf88e132333439d5a71c9165ff1a6f1193b2335eacb42b5ba5540def54d6d0960fa1b37b655040b594c57ec6984b273c58228a9bde8614b8f6bcc874e7dbde109fbdc41684051fe05c4de5dc0bafee37fb2be501e36e4d8ea55864432cc554a01c4c202531edc612f41dfbd0c220ca5b603c7ed47e9be4145b1490161e31a2e6c82eace746e62bb8862a1b85d35af5286587c3ea6bb72444dad67abee707a9d583455181c0b246fd0c33d3522458ca4abceaae209ea5464d294837e7678150add9a37f3405a76baeceb0eef3f3fe7038980e96652c5e53d77b15dd8197f304bab443ca94d66f43d69f4c7ec1c7bdc57093893a67d20d89e587a9988e873457cef966d9270216e65f1eae2ad0d3bff25650d884ec197a69e5c76d39d77f7e5e1c623bd664645ed7df0b69e8dc3aa7e6260ed481ed07cce4405de34313561fa30bd3a78a7bed156fa773c0405cf20d4b0fd1cb60d58a2eb136dfac1e388def9e81a9d1f1f769be49d1627f206364c61c5a09d9d35d7ae05f8eab1d0cd633cc4d92fe1fb58ec4845c16e1578e80c0702471577e49dc9e106dc59c02b534f59b0c449a2ddca0f5590d4c34c5f877503094256f017c9a4a0ba613d136e07d7eeb3a4cc23121a934ee84249820bd64987ec55351d3602cc7afd1ba272557e531b3371b99df3528dec1f8d1d4eb6d49ddf94027b9390eae72f740ff649a4438448463778df7d5a0a5f764336a2efe509afef92610de6dbf22cf51381a3243171cfa9ca8caf6ac9f4e0ea6238cd7ef7bbd5fb06d3426aafe2bc94c8776f5ee74725b2f7f1e91b533ad9ebc626cfc5d2a6eee2c792602be3e838f4fe43e1851940a1e21d2c4fec3fd672b0c7d3e266bec169315a9960c7c433517484693894f71328b5738127fa6e51a6b0145f7316f5677581146028bfc4a6e3399037e6cd038cb993bd1e63b7c5c9020ddb57dd78da6b80b9593eb0252b499e66c0d3526ae41d19f9430ea7de2c8dcff825ab026e4bdc2da80340d14415f22a009bc7b325d615855a98b403a22939ddefe7d82f9b48814c09f4a121ed92575cfe32e04df38cd041fcaf0b1e572fc18992c182cc8ebe05969d90970ca0ee9b651fd65a4d002490f04639bfce86058cd1ed219457242c4c233e5a4b84cf3e62f45cf2f2e308b43ea17839640a7e93570a4ae3760ac79ee0a61290944fe47ad43c67f01be52d639c85b3b5b07a9aa98b2e7cb3dddb82d2a0edf88c38b4119bba29cf812970bf79629d295c2764a3d3c8b20743e939102f6584458ddd1da577b5667dfdbb350b596f565d64040eb16e6b00a1c76af2e6ccc18273c3075b5bad660017506a24b8fe728cfb6531c16c92f6726127e93e919bc4a018b29cab8b098ec0390106d37e73e14d647c00a723191ac447819457e1882e7930e147aa05c6a6648d073baf7e0a379cf4a079cc2aa7111ea0b81b6737a50e2a02c3e862fed198020f295b1f0a2fd42a19d69599f3ea4263b01d0899cf13509bc2f0490c78c69cc8182d009bab3a84d12287890ce43424ec7fbbfd0daa76f08fb3e0bfee27688ef5ffcc366701539cc87446ae2a0d910664626992cfb8cef95990e59ab088db3cf5d8303820ab67a3f463c8a38c054e1791c1d07e9f2e14ee8d1138ba4b2f9763fb9faeac3679a777c60048341a9c3f370b855d4125e9597db28248e3d38313d18e5ead92def8d04f7b286c4f05064a0137a1821f5e5da38e69297b9fce7ee55b595ea05272509dd1dc96fb28e35547e2179808d333aa357fe87dabe64cf1c3fd4dd75bf30b9bc7d4135d3432df069f5b60e152b287e3be28f552aa867b58e47574cbc5facfee6dd2f585356bfffdf2abfbb13cc957a56b0e47225b9df42c4bc44f7da0cee1b64d4bea4d7142dc18851a86eba8ca3a632820088d1d0f277612181d33e66554a538228cfbb163174114fe8b5b143edee891d6cfc697b0dc486b384db8cbd20d1b48d43d254a5174adfa368a793f060c922a59bf309954546648d174ad56f045f9712d2a064ab37b05f92b71f3e11cee5d6709e8c79b501b103db8261574533fcde6bc4c26b257ec4f0a3a237b9ef488e17ad5af95fcd176452d1b7e20106a2119508d9e74de932ad79c4c608f1ac76e95b9ffbf9637cc0267b8ced6d139103528fde29e1df10e5adf50882053986d1670d420bcaa3365fb662e0d433508db556ca2a5913eba6cdef737e856c2a24be7007aed60ed10fb77b7e9b339a1c7da723b3ffc12fa12a012cac3b0be1e4a317b17247b5f1102e006c745fda650ab1ace51adbf3bed3c15281ff975e2e7cc58b7998ae0dfde1e0c1376343d0dd0aadd07d8919c296a7cbd4c8265ab3e0a4f5c1f6da51d946f9bb2b00bcbd134335691ff4e797e9d07779493d88e1e91b033479207e36e76f1d4264f551ee2a9a95c33f4c588e5f3d366e2f187d8eb77ebb3f9acd757d7384aadd90392aac2b230804502cfaafaf2328420a41c173d903996cdd87366a33efdf9d565cb0ac0078b2f40cdbf773b86ea3f44eff5b86e822b536dcd72006123a4e11b26d21120d8b250b8dec716b091b1eb150857bc64759f31d7add4b058841193c58417c33e9238f4741804f9f148096b15ce8cec22dd2c4636a39e86eb1325dfa7ac87e58d34950a309b4c513a7e14f00cb5c8b6bbbf3487b3bf9f29ce445a9cdcd1eb402724b2f092ea52ffcc3b24a482a24d19f89d44afc6e166082bcebafd24d54bb5022a08a2469e1633edd7832e6c74e5d9976beebb6928620c9e865991d093313f4f24d7e8d7e63bce3e4bc8ef79be24f2b7278dd84e80801d304cf3d775e53e8f670fb0ad4dada5fca06536f31ab452a7d1dbfa032c2bed87b66d0a7fcce064a4a28b60a7e626605385b44fd54286ce871212268ab65415f69ad0f60062f8f7e46702d5f54895d9b2e7c219c30f1a32e061fcbb5875af955f73f2bb31db7b282aa12dd0fba2c8e469aacc6914c230d499c8b7ba92572b19dfbc2ebaf4ebec1842ec54ac05d95cca796beb8cf14f5fbdd8e4baaf2d59833b5a329e4fc3f32c730d5fe55124b1a7e4fb7f64f63f74cf928552e4b5eb1f2976a99198925c0ccf5ba8ecaec313d216e7adfb1fcca3693b9e74b326551a0528c5097e1f34247b5eda7bbacbeb21fe6491ce67c3c7764cd798b1fa813454bda9e017b1c8981d95b3e65782f89514d93a0ae33ed10e6c8897a3cdb9d01cd0e0e12f3b398ec72409a4adcb18c7f6091823079782cd68ac017edd272464805f3e831e06f02ad1e1da0acb7cd6807a9b80c29fa3b2ae5b1b75b43425041bc26cffd0c9bd4372925f3ebca751480063084a888fc9c0bb5243dfda4b4a9d4f8dae59f4eeb768788e4db8a601082ffd3e09d9336d5f339a825676c3e3367b161ceb3a6cdd80ce650fa711452e886b1aab4f4a92f07141ea3c28bbffce0bbc4b4587d4cbe42b3dc7d92d0d3298dc751aa71ff7aaf353a75ae3f23363cd925247660562bfd43348a7fd2fe33b2c46196a0b7f30ae843f32c3141e7cd523963162eaa726a8ba34b6f34d8980d8a69df2a103e0462d9d57faffd6c1bb3967b877b9e319ad357fa05c25c27c823025f246086de0f60d668930a5ffc304a026dd3c06a1ca2ff6328dc9046c28664087cab513358de893da3fadb740618b5e0086ae17aa8ae59c2a31592e21287e600fd14a4c57b285991398298944729029d979811d3784484e2d09a883636be1cebad5ae042187b62fb858e47ee7b66eb0303c3ad215f3f1877e6298bd439d10dfe2055655c68527efe9fb5188a3345959a36d49e3fa5f05d1bf880cba1827de4be26ff650daa1f0a4727383b2601168996303c550cb8ba0fed5d739905adf7ca5655d34a953cd6a6f47110fe9330b831bfdb79213a898f1e1e719786407e10bea78527c94799458c64706afc3931426326bbcf2490db4fc85960da9431b9028eac16664559c32e55ceb99c020126d65943eb4eaacf0fd642a1fdc2885a749a53e27eec2dd4927ca58cef7be2fddbe7e3c6a9b7f8a1a047e1720b36f92cee110c5cd35fe4e5fd88902feccb964953059a00c783d7f8e480955bdaf448170d2003e14ed223c2b9938744e1c14a619bd67c306df9bb00620cb300882c2c48de11e512fe4e249ba69aa527cd718faae2989b9306144e63ff75543e2a8ebaa92b19754a32ef4d4174c8c25648aec0336af2ea17f85096dce5affd0a6da6079cfa04c1acadda7f91f1a15f4d2fae5b3516a69c4bc0664066ba697de2de4ef233143119f5ff216c68d9a37b6ac5d24f334851c147657f0de1926c1b026b7028d54578e46383b4d83feb0fd1e366e682aedd5eef12b35ff8e192f82d59db1e82adaef5f0e3138d5b408e5948048df66f20501013b5cf09755484236c6e20e98e9deadc7c6280c463c002c274243b85fbf8434200f8f6621e754a8b1dca5a366bbab3fed0e22cb235ee6d8756908fc49a72cae7800e9cbbde623963e12a1b1c01e2129d65ff53d6e363da80b47323d15cfe7f45bb9f3c1619c59053584cc4d68d586c35540a907d590b7e076176609687af7f06f811c6393436ec08767fbdfe08145165869dca0ad295de1d4b170bb30b0184e92f40b3b0225aefee2ff8d3a9a94827bb185d48bb9dbeb2169a377b9a9ea40666eb61009a41756c7190cff6db52152119e97c919f9a327bce47b0c8ed157207cadaecd89375155c9ede0ec95dc336e038cfcc3fa6a14ec85371cff3a5e28086437c5b4f343faf76f77980f2f35d9592b9549eb94cc402855d1a88bf04a67484e9a7b7425179ffc5d5f8eb6474dd76d5f64c7cdad8e002d65bf34b9a7f597a515e47060a21ec853eba88bb8152ba22e682ece98da7b90b085a80dceab5920668cbebed0acaee891d7b16c5a5075fc007a63f954a5b8ecad07ef54ea9ae6d67305591f6042fa1fc5d092300da6c97492653d2a4b1fc3611f9e7222451ebf33a836d153869433466957717d5a88abd01f94783102efd01116d4f25a5a64d8ad5946f4e798e9c4cf7ce615aeb0bda6f7c9deb632f3be4283fe8e7a45a920e15824b8f354144b71dc580fc6c1d5eb5768417eb99149a177f07dd61216fb1f85e100bf2e8c5c4d73055d19ce09e970085d2213617b738ebeebfb386b0765e2a5d0de3cf7cf147859cd9b5e28e388f2b9d1de6987fdce0fcd20ecc563331290fe28a4e1de9c327829c4c69bd18745a8ac8f9a0a8e4ad65b07495a63b20f4b2f4c8dd49cc43d9474072781ca60637837eef9a2c9596701b607aefd8c679a3c714fcd9a85f73108c87662a37376f67e623bebe30995f02855d216a4493315ef3ebe9160d9ee0c05cad991f7f42def8d662a144d5222a1f3cdcffc0ec111b3ea555de6726fbc4d0893bc87ddff378628049f1fba03647d13ee4f98815843c619b9f02faed2d3fe3da8bcd4e839ed749e3c5834597ce1e2892ed0d443d8d983aeb8fc69ad80184e69791cfeb634b98949a17ceabbf7cb2e85f7d3d771d5c9a23f327c4ebf8ad8495830d2f2f43b0a500476c5a37ceb9d475a23f15c69e07d03129265dd70a83ce8a5cd40e929c2494830897e41c62de0ff8c8dc9afdff4844b28d4acc376abf59dbb0d08a2f01e7b622c702dc514bc277a1129619cfe00cdec4bc9a60383f43b79587efd3eb9bba9637641068430ff7e7c62ce572f1679962ddc524d200c07d427e6db62657ddf872200751be788d462723d1712e06017f6fec18ecd978ad377d8db8661478d2cf60847baa34dc43e1910482d42c02c1eabf8f3a3fa7fa8566dc61756c246c44a2bbdc6378ffd6dc8e35242a057c7b2751d8f537f6596426be24bf89cec38c7133fd1ee024e53321bd787aa1d253b608e52328240618da5d45af03710cd1613211f87768ab518bc4f1cd01254cce9c151b2ab653c1451c72b9530142fff1bd6f409d8f4b4cb2c589b7bd65a73aac8980d9dc807856e37edb7c31e27fc7eb812d5c39d8f95ef26626ba580b03390c180fbdfb2a3ab2d6fb563c51f49e9a10f25f46289e74981e019e02775918fbe9a8cef65586cac7b9020bfed88ff27d60174f73570053b1bfda473050b015427b14d561f773c2f5b4ff4e7eca0e4e7177e811e6eb60bd72b1c0459d1ef6615e5038d7fe06847ab24a7f41d1593acb4b54d8c2fddb6ef23d62b0246cea17a1cb2dabaeda2625b1a64b9da3b88496349c41b33935e39700913b23ca60b565d961442cc8c7a8991afe7734965774eed9fd267fa62ecec64f32792d9d0fb19d8e666d852466da5a73ce68275785b983d16ff3849ade8adfa672743d923858e886fb344f653a0247d40c7b0b87a7469c48d6056c340eb8d7e2a04b90930801424c6b9b2547486595f505a89e1492d015e082957168fb103d11bab79cc4852638193a855fbf35294ac690bd295f6fbc28aaf194e13e4b76c6d05772a6b2f8513ce2c03d884fad0df46228e19b940398ffb09c3d5300b4ece8e71bb25485d0d44bdbfee6655bec314759e46e8b826b98692b31bfe2c817e4f5d6cc87e4964770adc9c60178e5590b31e9880c80ac9125a972b04f4e24b1eaad78a691f70823dea467f61a7b308f8191f2b8b7c006e92d950ca5b8238f20ef75c28f81180ce3ae09e72db1700740e7a252ca957ad52c4a74eb8207d3eb9d053febbe48b8640585a2106857144a1c2dc3d12af55a97bd93db5dea31a28bb94a64a54947f9443c762eb8efe68499c9ead28cdeadc913828f00c82fc4bd96aa32ff31975daa9c61b4b6349496292d9b75a6f0012280eadd9853ee4d0130cc57b0d24a0b8115fb5241037e91b93142810ba2daeb6e2ce3647f367111082dbf7083854d395b47999026d112aaf90db3109aaecf7ce10e72ca13d5f6da166e9a938fe439230abb58abf47d72f39ea60436f0b8f939d76832abdf58f07d90776f29101b99c808d44ccc7fb60ce155cb3d2a38e5d4f215a7ff552cbcb64d964f1c9e32242bef9175437cd507daf55dee7e3d48f4c0c5799f2a692005c7783d11eb84a533e79a1dae908b2e8b8924b0c9819f190b3f8ce61bd3236b2d48025b2e707a2a64401a821603b79329d390c3a75aa4de23ecbcf7d90d32d6a42c160e02843ed80134c3c542e16791b0a60ac2d3df46e9079d3eb37b7d220513a636c9437945022c8b9305d90953d478bcb956c28556066200109faaccb090c4084ae2e8126bc01c9d07a7f6cc2407d0c4a56f11b2d69780ad0c6295cefa259169800019908cee9d52804c16fa34b4c5e7feae98aa5e1ce7c7365ba499fa1622bb5338b1fd951134e48e4bfabd055263bccb2f204a710dc531c7abc412c92cde71b096c9d4d2ebf98d33ff42c3a30699d14f111062a78b5090bd27f3cf3f6904d81d4cb8a19a924af0dc45253d370c5a1e7dce7a2d1457dbf59eba5e4a17545d1c86acaa4805e4aad" + } + }, + "2" : { + "id" : "0x41f547a71d1404f147606491f3b96e49f67d4658", + "keys" : { + "falcon-512" : "0x09b3f618864123704b78a9552a0b068f395444870c2cbd5d741a0c565554bc2785a691ff502d6ed0e14c925eaa24c075d20949d12f369848e178d50b085e87c9b251f68b61ec506435bf5b53a49d299409f54e906daed0c1a25a63563090f6660380321e786311b492bd7ff684a2650bec34d5bdf071c49f5a050415b2c3579b2994fbaa9a97b7f24a30e7e8bba68145196e0424471de31775aeca732dad506b16ad4f61a325136a6664f439e60687e764a009db3218f706785b98b154a625c168fa2ac541b8dce39682172b95e19424b98ba985622b2a23d8db2d46a7230bcb667372a924aa4d1a9e9cc1a8dc1ae11fe88081d74d857bf63e9814a08c9ce059a4b1ccab23a2fba765086b15bcba135d0937a064b90663ec69ab521746600610bec0a6b3c705277f90d588a00e908dd075059de6c52fe05fd8fa8ba1b3c2cf151cc810225db79aae0328b8ea1e92fc67b7bde405a1442128023128a5f7089722b954f15597ebba3aa3b55ae947be1e659be52af04c3205492387b51e72746a904ff13ae41964281ebd81783528269ccd6d2a7845d790fe4cda8ee58d93de3a3ca28bc4e6e18e812c313452a4468049a86ae39d5fe156fa2a43d29161bc911d515f4cb6b625f4679e37d5b55064a16c3e48a7f3ae10a09d9ab7f0ea4dfa2fba9da412af1978720fc47185c7ae16474833c8d278dca65b822c5526bd4da67c0700166cba95e3d23691e6e1e86ea4e560cddd59af18a689c4dc2b6dc0e114f2a84754498a8e299ac17a12fd199d1fd8d5c31b0a40ef4b2fc9627d16ec11241fddb4fee6c652d0119f7d60e852decd3489cdfb75c10a18f562154e542614427ee7cb0e96b99d74eb9585223760bac37de51697d86403ac877f823451d8c60e84188a8fa6804c3a8298f223169c4c61fe443434a76d6a51ccc1d06df3b962fdba9e27d7723a8390f4672d66dc2f756263ab928d9d14f6d550acb286647ed6dfb091c462cf4756194ebac9c94bdf7ae4ac7b5093ab9cb94455dad8ab15b134d31947df478a1aca5e28e98020dfdb6d4ab750911bb2690104b9606e78c8659c2f511bfa3254d32e65aab180ebb74f9848700a8cba3a1a546b77678a17716f232e57b204157761161670bf654f182a14bbbb1c16b14b652127241167e898e097b6270b062f0607a50bb040868768a24312a8da316dbc547237ace3a160b7835c805061f8f2723386929835251945908058905f625cd9a91eea83c0f693523128c68613038e", + "slh-dsa-sha2-128s" : "0x9fc24e59b10d63d59b6c448f79d029745c4f27a31903ad2a6f6fdd09b3abebb1" + }, + "pop" : { + "falcon-512" : "0x39916580739423a110970dbfa8b6f06ebfe827b6503b1629f4008d64024e63c71af7a932f3f29cbf08eae4cca92f36f670de2f3a0a461999f431bd5da16b868d2230d6403158010384f0f98d2b95936e0a132f5b86f5d11e32ecf3cf206ab9d26c1cb7fc93ea3468109a59163b15391795b76ce450813b28d28510767270c1c474632bd6eab70174350d329d0039ef8fa6739b2f665fff021cf669156e3797da72e5913d08c94a48a16d12b5ad7e521b730bef92d7cc277a92b3e4c9f9cfff7cb3896f93dbd495637c2a47835f2afde4222c8dd36d4d6d9cb419b5b5563bcc1a61b640e70bf2c4bed6599c5ec50643fa302c227ae56adc3d4b044926f4bc4b26d58d0d0778e2b8ec5e18b5cb9ecb6d7a26808962082bea23a932afefd0d483a9952f98ea11b629b0ad74c367e09cc016b817b624547ed8fdf360a8a06acf7b5f0d3c92448d9f7e9453a4e29222611490a7188dcd0a8568b67d333228454cd9cb99fdc9d6d945a64d8d96348120295b8f2ea9e95f92d9dddfa2a91b6ea2c44de7b605db8e3e1019dbe5cb464e0b211713cdcc32ef3631caccb48b6b0c968492a5f1288e99ada3a4d8287b5c9f46236b4b3fcfa55135f1ee4b13e3d6765866a316d8cb3ce80d67a518ee974976172f91eff1cf973631ade2a2b81134c39d0f3c72bf4fe546a7bc7d3f1142aaae3f5b4e8f3932d9b516ff1cfd827e7395d576d96cff52f00e76abe5ba3f1f0887fb064ee11096e52d441d1b4dc7a69462acb7296c489f498c456b2336f2f112391bd7e545629697bd096ab373a34147643199222b8f1b05a95bba5877aeed29c43bddecbc814a6f2bb38d1dde30c352e06f19bf52717236488848599eaa1ef17873681e91064430aa2f4a23445fac0a8c8231c8cd7c27f928ca0ebf7ab08feb53", + "slh-dsa-sha2-128s" : "0xe922917f6a5d9d49338e445586f4b3bfcdd92b89a013edba38e598aad56f8d10875fa62dddbce6c66865eb7d14067931d640a3b7c5dc448bf7cb6309943c2f0930c78c03fad2cdfa1c21689c6f621bfb1cba1968dd28e66ceb6cd5cd4f10b8797a53a8ad54e93febced4dc1c48fc4bf768b85a811866f86f004fdc340628ccf44b295fb5310898614b6a0341a2456b94930d086ee75f67bb935e62c739fb87407975f78f372ec11c0bf8bce5144abbb2678711850174134e6e5acc3b87f14022d158a57dcc7916a8e75341488af6b1d9c0af90bf2ea86440fd1b8001d7fdb8ecb8cf392498e41f8be5c7aaa81841aba7422c45fcc0caadd52d36d1d56cd02d027b6ed9d122ab3d53597507079bd9fb476105b12e3406c1873a8bf6a5b9479b5cab9c370b79425b1bc7616e2f1f66a398c2e38f7a9ff1077918fb3f7e65f025d4feb8b13397460a753b1999b3d351c5b77ece3751686c02db0ea4ff23727527cdaf038ff060a09689a13fb7dc902cb82844b11f8092625272d8888090e60cb1040b6cc5b0ceb6854a7fa769a77523e7a718a1a9cf56b94be12fe8fc4d319b335757c6b4807e17e9ffdbb486d9910404e675648d069c55e96894c7b2ab17d91aff88f44ad609142ea3747117596c98831338c616cea7439b1754390604188018818a5fd2cac7522b2df5e42c14e77b7383744234da5fe1a02a60e18a178751eb9c0212cd847317d64dabe4407c4a4d2ccb6965eb33699c37c733d42e66f92b01cf8b0ba2c8d737711439de358756ab4ba374e7359f8ec5838f531c984e2903026acbce89ff2935855ba5e5f3d85c82bfc3975ba3d4a9721ffaab1182035bb7526f062466895c5900f854fb52f5913591828cef55cd07d70c5805327cccb9822860f32a8e8f78806e39d36ea7be59186da6ab5f1739b6716323b9379446ce6e50345cfc9b769162ba96929e2cf0693b77a558e5386945fe5c6410a2594805695a4cb602fb25993a0359753f4ecea67d0a1045a80054664a5f589dd823d11b1e440813b3bf141f119c38f9595c1eec85ce74fd0cd69ab783ff7adf32746d3c4db1153df20ca1ff94acfcf3c8ece27b9a7819f7751028435730771a0d7eee4d5a05627e9577de7a1c0d16400a81c6d93fe48990d8d4eb942233b6e81f2606c228423d52fa012ba6de7c24ef446f32ba1e1bee5ab78e79f40ddc3f8279277735564901682f03ffbae6176aa8c2dc6871cdea56cb875f25c88d955ad2e5f560fa25acb5d2130327aff86a324aaa721874c4276667404738fffa534f26e4e0dd67a9d0e5d447c2868af3f5b93dad621f528509fa6c3d2b9a391275396d3db6487ef219bf42088433b79d34ce793f024380319f1a0017c0cf821d998871fd35d2da47b774f6c6664e54090d74d087fa43ca622417314ac0a7f2d23da0e9be5242191b9096ab0e200bcee276a787ecb770a60f975c2935f24600869822ce96462d97f34c23eaa50b443a504670923b260e1698eb12a76a094b9a8b9bc2fb07c38b03b495a70e4976d05477ee88b58e2d66729a91f0fc003e56236c5285869a6eba073b36efdac3999ca0b91408282f83b179629bebabd5355381c4435bb173598ce3496dd8616eba46505a64d29a166b51b1033cafccc0c6cce63afe4f2dff7a2cc62a909853b14b40c418d3a180a922b02243637700c87bd0268e3ba09730a2e2e20065a7310d8cb86ae6b2174454ee4c01fc86c0441cbc51f1801a021da1f1fa4ba6cc0e8e35a11a0e55a4aec60d5c98330ea1822991083deb2a80efaf382d29cd0b04fc2b3289e024814281aad94ffde1a837da103a0022b23f223e63b852ce8bb89d50271b29b5dd805edf1f4339bed98ad4e4fa42cd726f3f70c7c434035cd9180a02ac954a1173c2694f0cd59942291c34a8071803a69552d71b8191c4299add494d05e838803e5e9de4e5a2d7b0f8b4a221ce677b5b365e57284f4b74c973c72a565422b2233a5b3770cf28645a18ce1bf7cd0e726566c382d801e655829a7c5e2aa149217075014492b80533bad2c8013c382aae26cf4dc39a6eace929903894f85c8e97e4be1df2bbd53fc780c46ed384e01fb40c515e88bcbeb2005df6c152f0d169a90ce3a43c2c3469a85a456991996f6e9ffab555233598f767b38307fab61336a8d57c1f6753749d46e1b3032532a5ef11082cb30e12bb6b292d0f37806e6208d4bbb7c7cba1b3b410ec1d29dd35af7d38ba79059999992088f06b587bd9570ad0a1e2352a6133a50a3a45da27fcf63cb7c6fa98e6d3d3d7987447fc596edd9a157d72d0bb60441935481c54f9c163b985c708ea1dc510f35f9aa8768e73843ba5e8621912f43383365f534f3ceef986e90664123079f7355e5a5193b0ca1bce0443e01adc00f18f8f07a9038cfbc672494715221c0b8c5dadf7a66875ad67300e5fd662ae7b4281f2df29aa0f5b7f9f70fd3180d7875aaf784eb3da36f9fd8abfcbf2f156392cd196ab952669a531bf941b48b7d2850cb8a66addeeb1447d89daac2079094bd4163d9e0d8ed52e9a93e31a7d4f5d2afc4b7d2c0b988d41c3e247237363dc05dd16d49d159005b42ab9def7290a51beb7811e9f4fb9961a7af09fd4f291f797f8eeb05bf76884ca340af07f62f587dfcbadb344c1b350960686aeecf772f59d3f5c9f6c01240667d1fa5c3d84dae1f0dc9562543e4636d81a3f0407bc29127bf4b4527d0f6d7815a941fb53e6527d5940739bd369786e710f6b0f69b05b2e0c49bfa51411319b9f7b172fc11d901e41ec54887a0fe5748b1d2403d23ed5494b96639126c1d3fde11d85c8c717fe8b01319a9668b109e2d34a6ef5911aeb31c88b71c32a6723b0ad2a28b2d8c75087a546550b0971dde64d3cd69434914d5544a8828c6e7775cc802fd71e467e0327d692cba302f681fdf0c7425ee580c413d1a3904974e55e7ddb19de793541a7a4310e50990744b4655e72b4eb5a3a8c5fcc09a545198c6475dd26a43338753855ae4dbd845d3122b16063b9d4f1170b0fb1eddbe15bf6a0f935565ea46bbbd6c00491a9ea8288822dbfe8788aec3eb3e5e42140fe3cdf16328587b736242c2aa85828dc84352bea3c6d985ac48437bd72f12adc7fc08d03371992547b91a3061c9f9bc6c32a8555df262c14dba813ae8e2f9c732bf7360b1e94498f5a1248ca4a0c020e381d8c25f4924f91bf0c649207b1314bc0088d2f15f662a2594962596ea66c036677c451e77f051cf9c5f6b83ada58109f68b415af541dff5d6cba732a938ef4d425243a3571f475af0a456a7ea5afc7c78fa195780b270ee1f4d69fca7c93087e103595af8757afa5dd23ed17bd723c1edf8701a1b4bcb2102ab2aaef2df7cf485370d310651f6e897cd2d687375d29c7f101aed941cbbbc12ac7ad60c3147e41fffafb766e548f0e96bc911f38a6d1864277c170fae28e4b06fee1d1925923432bbe28e41f8cc19271f68196bf4724278f8492a6d5c0826e03d8dc1232c0efe5c7598b71be7d6c468ccb1077d7c8bd4630bfa4a108d58d5498e40584f6ab690e27ec8152c1c39bb70ccf7c5f2c33e48af63d536d9e7ba90db931783a9cccc05c22dbfd90f84f75d139aa22b1246a15534f1521ecb9031ff0686c39b728fcf6e0323cda56b641561a88c6f78cb1d880d102d26b20213d03af41c91ba6924a0ae17a92fdb375492fa0b514ce2fe88fb95d72e5b1b11bff99d1b0ea0832a0d3472b5880b3ddef0bc0891833adec1f7797f86fc549b22df2a0a59390a900e588cdd2405e5afea987db98554585c85fa2428cababff59c17713cfb100084192cee9f4ee3855163ce76fc59ebd4b23766b0ca7c2d59754ca42ebe3c997cc4cdcfc2ef455d3afd488474b4da2a6ba278c196616f995c4398710c291efccbe36bb724bff2d097095f440f79917229a6fce31dbd89e58696c4571dce1e535adf5e1b0942fb5d59a13f7fb4eb17647e83e587acc50d920db2951bb48e1495256e10a62a8d43d3cb7719c54c24f546ce4937e2ba7b1911f5684068c365398e41f7153cb1e2b9ac014bb7395a36efff14ee94373dd128d725dd6ae6af408a5da12ee540fd8d2264b24cc7fffdb2859ce5227ec3c3d5e0bae49b5f495a39e5d6b3baf7c967b21a55e3528fb0bda0e9f2a3cdc37904005a76b07d044ce439a345f5ab69a3db8c711d478288df2d4776d53ccfbf49a06d0d0332814d34053c98ee3464bbf46309b763114ddf1073100c34466cbf3414b4bc7b8ee73c44d76c46435c0354a71fba7d8ba8b0248f867cee79a4beacb5dee9c45577e5b423582fd261b6164de192e98aaabb47179a6fff8827221a70c77df46d01357bf27392d4dfe3b8e24abe9854d3109b533edab16f495eaae07562dfb4428cefbffd748f144329f7653931a9f65a3b024bf19d2fe04764d53730df662469a19c4bd966dc925b1ac52d9094ff7dd7268697468135000a8766d8292f9079f9bb5601193df637f8192812a9a97f604015ec15c89fee9f700509cd745da588a86c7c756bc84745c593bcb7c7d9d34bea98d505a57b31a90833e3d1e154c3b686d5d8a20375111bfc6964bdf2f815e160754b56c59fec8fa533cca9e21d4f688ed94cdab427f9e179fd308856ab7f7206d13d65cace5f8d65c8b95270559a7d33c76c81794ec38d75e2b3f9ebd1351e07da0540e3981d447558d6c21ce6b0c32bf61a3f554cae9edac7a92e2e25965037830c57f30a021953d91c8349b94181ca8c3bec9f33327caa08e437e542d4cd9742ea16801aae94e19442030e8c95bd4f23d88ae9a1f1cfe4a80b046f2e54885c54572a215f17bf8359db4edc830c48941153bdff79a6777a67a9ca0ee3c5d2e4497871f4797974c1d932fa3417b909ca5b29dfb315bf1590829f1cfad84b9e068213b8a6e9171c1c41757e262d371b7073d1b90fde5bdbafab76426f486581bf52fb2a53df2fdd0a6d5065afa6148606a3dd311b155dc0dcaf9f741a50d105053225f1c223ad0fd8af47d27db808cd32f733fcc8e2655523fd36a906c6e6a1d6eba56311424458b446bc1b77b866af76be210b9a417e87903a1ba2e431c176a82cc5a71a4ddf29350efa6dd8d1707d3884b31b9b52bc876b6599f40b077c43d815d2e02ba043505abe7ecd7dcf73818ceb77a09ea4e582616f4af11133a80429edc9a434f3cd2b41192edad842b433ee7936fa5c7680023f0727f4337c53912fdf6fc7e6d5c0dda2e0a8722d5b0565ddd8f9349815bf38e8615b5c7ae35622d7feb7907e6423c8aa7bdf57355c0353da377977e180b88ecb1faaeee2d7142f342263e636eb99c78af9cd0ef0314d9b57fe1176bf8b13c221b5a05c1f876194ac26c35b0ddd91a2130276ac402314367d99c2e300a33073c06dc18cdf5361908d58a9553268c6d0ba19ef6f6d0bfbf2b1e2f5d284c74695189d8a3236e927d9598a4be253d0492f98015851be3693b0c1b3c4a73a95ea57c20699240887d501db5d3525e787dd1a583c2a831a9b59886c873b8ea5dcec376ff37fdabfa21b74b3968008dfd9784fec35696779217b4f5507ea15852f751af31243dbf857d66cdbfb2e290b52f67312fe5d93bdba25b1b7dfb0f3488974a7ff0e437cea09d0baaba7bbd2506fcd182cf0dabe7c262a823fa1dcfe00bfaf7cb5f54283763a45134dfabe9927fecacd648ef767fdce12d570693ca6aa10c74c41761f64f31139f3aabbabefe6c7bd79a6a3334cb465f4203604c0288c49db25bd25c6c4c113e09fbd65c0f271f4a373294831c33f7b2ef392e4a4b91a3973d38473b9f3a485cdb04b7f75a833e2c0461c6f27e6d8f518d3a4366ea3bf37f43e4408f00c4c72ca8d0a9fdbf1ac7fc9fed47e8a3f3f33ebcbac25fc2e70bd39a85519181383597ccc3503e597a734c98e98a15be8bf4b37330f585ea138e5a2e4104ec1cb6f5a23df7ad83751c32432033f072e8ee3cdeaae4e7de98f9e58633bcdceab69ff60004cfdf51b54e9a33232e1df86ca9708811a69b8759e5b2daf57eaa8f1f74ae6aedfd9106c105a6cd715b66f561c0a0500a501a9f3d50a0b5540ec96d24fa5f6157f5465d413b204a224e2b752a49322a90aa340e4422e60a449cab5b6106135ad34ae309ada87ce9a99cab959f6c2502b2da99d8ef296884f4255fa88f38908e4294f799afbf3d664900c5230ebbec54328167495caa99942c13e834da5c488f2dcbbe4d2448eea2657a2f82a91a53d09b2b5445de8c8c8ede11f028e067620f2ad83be80d19f3b9a9f3b81bc8a405c233b2b17ce8ee5e0ef05847aeb36f219817d735faf6092d13569cbf069696bcbc50cb50cbef921f8fe76775f1034e73bf04431b696eae4943efd78ef6c19436c76ab389825e57b23f4529fb848233833afa418d85a92fc865e634357071bc415da56078f44bfe306d3b00574c04b2e94d6b801d5e3bc03686d1bb27dc2753112b2c22a43610a4dbe1e78c9f4f6f2b4ca3ccbddc2ce0e78fa34cac5935bf0867bf14f40648c805876e61fa841292ef2d2d53ecc00319a6df0bb6347dc865e39d9da574ad93f9cc910a9eb920986d5e6b331cb29b45ff32ddbc9e09b71eef73c8673307d6684a34ce85ff3a4208741b205d31011555b17b7c5effc210ad9309468a007d76983011b88c818eaeb3d3815a02d76f735433192f1bc99a6c7dbb57e107caf9e986248ebcb2e9c28279ea1e19a8f8027bd6f8728c9aed0b974322254e686526cd9ee0c6720de4eac01cd83e5705570c249856ebd7ec76cddd98cc096fb93db8ed86a5aa0736c36d978c09b915f7b9290cc255ea1a4b355888fb96e506cba34683c6a890c77d92fb01bba6b69f91efb3e4313111dafc3a189db063195bb650e0cf353dd975fb5c286f86b8f7d982ca5f74a78fa0fa3d693980be09298bd41c4ef38874ddb95e928494b9e345525b00e4760a1d6a4d54675e39b1d955e20a58f3ee65ffd1e249eb3558ba348a2be114f396c422c57314ce6df0ae9406dc6bd0bf5f1db49f012a03834b3314dec7839860867033643aa7dab3fe17ee025812ad2c05484abd0bdeb67043be2a57533d294aa03f24ec8afae512537343eaa16b07eabbbd2cf6755261ee473dc457ec36775102e1c8150248e4e51dcaa6abb7753920bd5247ccf6dcfed9dde98c9f37938d1c824fce116f39f0d67ab9c1fbc79e034e8bee6f79715433d485edc70e5c098ef7bcf2972171da06cd2f19cde8f824e0f2bbcd816f7eb87b7bdad659a900f36df708d3446d8e412b92142d8414f3a0508d106473e809a0831953e2064c655519a8f22fd0c517df7ac44d72a0e7b6fb697b188133fdea4d16e51c6da3109f7cec1a43fdf47957a35f4651454ae04da9e9d5a1668f46cbc58422c38fdf204f4e9c0df3b1f0ed2e14d0e008bed642353ede25a5c25684e7d593eee9d14b3a5a81429b30895ff5d1f7f4a124d2fc9bc1eda61e8e4bd73b57ef1a168cdfb1ce1d70955567a3d949bab203c379e3676932f96c04ddd0271a6afa0bd55988134de26bf5d6c725c09e04aaa03d393a82805a366e0d98e4b65bf45e05ce45f4dbdc1f8ec052102df08f5852630b706dcde9a3efebca3852e85cbd7fb6e6e00b4bb397004db1602b7f8c0dd365eb2d946d00eacf0ecedae83d26555a1bef8be88eee9de9f9811074151f0cc747f36d45ea7290d53c18fa8ce73f77f54226e5b1719a9bc911c9d7565693836edeaa6cdb7b2e2f9cc8c86b7c207cc14c261793df6c4c623355db04d5eac01c011677d26b764e9048bff8d76e2b6bdf792c57e044eea4307f5a3a62900df3f57ae88577644e2121134d61bbf3100fedf83371438b7854d06340ffc00383a0a4789a6a85062aaad3761dbb3c77e61c5926b0488f711a77735dce429b6e028373dd341fa2db5394867242331168665b4d7fc4ef96b12a571b8b25d44fb0de64b9820734e5647821a038428d67dbe95885526c3f2b8064e8baa5144b7fabbbf5a133c4b8d7a2f950a4fb69f9cdb37ac28cf0421894f0b771f80db988e25fb4ba9991664c93116bab82ba808693d583a2b697fd2b5eb673f954255da410df58231eb7c89b8257c5a68ca0306d088638b2faecbe3cacbbf77725e1685aa81d965f1ef663f1e474eceecbd52943c2e4e0456b52aa0c7758cd2fe67f3132176113a16dc9bd11d6dc03ee643fe8bd6677f2ec0ea754ab499e00ced482d78147ecb2f91d3154e03b57dfad4e4cdab98e134a609a7512a5a0e7c3c4adc9db444238e0fc3c3586b389d8d090ca46222a9533672eabe4fabcf61beaeb59c507f2941ced1ee4cd2afefee5aebeb354ec33cb85c9695426126f016929b471b9cc07dd6ff787fc2d682cc1e80657bd656acfb865dc91d6b032785cdd7a1d66d4d5463e142b6f028e8fb5bd6c621109e7022818b39197f85fd6ad4785efe48c4509e9a537f10d4fc9d23f275fb9e684c2f827868c9ff3baca8faa59903c829bbe1992541d15d538e64329e886ade0c5517cbab3b15fdb4e82506e27f449e268b488de32fd036fcf80f6c97505a5f33e4d3a004772a8970a3fe54049513cbc57131ffca1c82918a81bcb879b8f48d4300b11cd90069912ec9e59d10b6bebbc24ba1061c62a8762156df586a4aa758f13cab5ab59a3b1ef7d038c68920eb0a3c8a673c27b6fb80e5e8741b3b7fedff0c068a83e72373da07c8d73abbd9fa0084bdc92866917b546dbf7a6304f04b18d9bcebe2b4bcf5ac7ec526abb4a52a19094c8c231668d063c5558bdef0436ac418f42c5f41f2df996d19d48f8bec412de05a6a3b079e5b6ac1be0773814acc3d20166874ef64a6cf27f7fc72e0232031ddef1a6a4ac9897a86605550dd635c995626fe985aaabdd937bafe3e2a4082f0fc875ba7ea963a8e5987ad400423967bba1c0e369263ddf16fefc88901be069fc617ba6dca90608cd4a9d86476f8cbb41fc8197a9ee6fb331aae55959d107bef29b4f6f064651964999fb8dc904b8193dc7582fd47b0b0fb912f1c0c3838dd489c374ba5ea614cc80d4d196291c4a54989ae628dff5beb61612353ea0cdfd75962ab057bac887ce7f5df01861c769b88c7e1682f0ec6f7cea63598c3e6f41093d845db95377654fc56702a4110810917521ffada7432b7b16a64699e6a955a4273fe9220976f8b1ae9ea79678a3a74344a44c32454e7bbbae9a1fc440811693f66d63663c84bf847e0a2e8299fb0a6413194231a666a2efe5ae1c41db1cf12dbd8d58408cbeb1a9507b5b09ebf416f712b0040f8300dc5188e673c5ab1f13156718031f25cd5156beb8e77e95b48f42d1361da25a94cf1bb4a2a5c41f8da5c3f6a30c00e7d0323f7bc14583fe20815f468928fb69a480673b6c775b2d1af6a176abe5f6c679aae41ee1d5c89439a798823b65ef718515ceed5a7ec87eff54479a9afb48d0fcdb9a240e946719598512220d2afb20840e3680689cbbbf096e7d7505d1d854faacf024935a672278c5dfad333373015b11571b2a7329a0d2fa792d838ba3fcbe5303f84cc6d0454b89cf740bb0f83b79c98b38fa17dca42c5d57d8e86cbe16d46c4c0caad1f55ac4a399d9755067b642299b6b023e47ef74343b9d856545a51907f86034397dc1cdeb0856ea9fa753ba82c5f7e44e4423684f1b03ec59e5cdd96c03dbb1421e89bb0d1dd570309d92519e2b90fc83ece91ec6b13134cad1272f0fb1092a21e0066cc0a783e566167b52856fa6ef12aca963dde340e94f04980279f67a1eaec339d0656c09a3fe91a2b1885f1c09e831471ebe78f2af2efa1fe5821034ae172082de7cf5fd8599f7c575311d35248f2b0e6342cf7ac4bbcd19942a74ae9a9ea3ef5641f70ad1746406ad9bc24a1a3ed17dfda75ad54fb920ee9c92b792b76d42cec2c22fd1bb0f7acfa398d82b5f016f2943dfb44ca1d96b2a54ceb7595837379d62feac76dfa610a82ec947047176aa74fa8d58aef3d34e79768ba7b213a8c44c6e9739f05eb197148b2d2f3c5a1dbf0d4ddc4ae2bf0eeb93d2d4d0990c2c21ae34300a3139ccb5cf216b4031f5be4fc61b137a952fd516e45236dc791b692910a09a600fce9c14ce9e429ef1ae3ae737ebe9921d10522a4507d0794fde66f639189cca95923c51f97cfde4d57c718b6e0ee709227fef69a95a229ab54a4ba9d858115ac04dd0a594ee8fba7d29a23d8df53c131dbd6f7f47c6ec482cb2da38e98a8bb51a2878950877408f0892199ae4e94c4c8eeb614eeac1435b2947ba67c30ff52ff4c958eefd318814a17f53c8ae691d3fd91ea8196bd82d4af9fa216e32636d6535d5c68f579d3066654f2cc067873fea5d56f9676800892f210363fc790086a778fb721854a0abf9a2403ac6de964c2eee9011a44cc0d66ced8bb0634cb986a12b0d5d875db0f26f008555f7ff57f42a441250dc5d13dc5342ef36c288127d46b4a2cd85e5189877003589579aa787c46843e47666be2ca6e8aa84e065ec77f0f77e5cee75785e2358a5e1fd7d540ba1d36052964d7a5de9d1598cb71b238c6a6f2a4b6c72d8c5b0a4c2f17342747dcfb2bf7bc52256fa21741e1f8e7c2b749005ef240296ff9935f75e943c2e725e9f54636c3466450a49eadcc6f330558f2146426fe6786f4ac6fed1943d616474f7665e8ecc2612479eac464c8313066de5fb652beb42ee17c0d9eac4496b5a732c3df31d90e24ffa8eacb12b72dc1d393d0880959e0ce9db8141ae1490bb12af9bb2d12da5809a348714024d7beb73dbdeb4d7adcb68c59ada159fd1855ddfaa33e0b985d7bc3f509e64ee52b5057656295dc669939c45d89a07f03c0062e20e309a0388725c993f74cfbb8db38b85f13bae8b27a4da755398f228d79eba2ad5101d80c75eebee608dae268dac0c3a1c63e311fd82cddb78f06fd576314ffbee8ed598818894fffa54b56e7a7be5c20ac553d106609f4bc757acfe7b116f695fa4ac3ac93bd0de4bf87aa6aca5535e467298aa934c11143bbf39c464db663b97ad416b6501daa12fa8d9aaa31334851f36f654356467cf4f42fcf0c4cb239c2acf7c7938c0760a6a8f953e75471881c59293285241f7b73e4b3dd5" + } + } + }, + "registryHash" : "0x05d68cf78b088d589f8b54243857a41c5a42958ae0e5807e3ee2fae9c6501b67", + "root" : "0x26da256a0280b55dada254313ab5cc4eee9fbd5ac2b37c229e6c044c73d3519e", + "rowHashes" : [ "0xea11f4ef69b54777a682922371576f1109ac0007190892350c1f2b156bdfd047", "0x97cb598a18b30f14e7769ab0a48af96205eee47a865c21ed1a27eff79f173f00", "0x15c5ae7699bc260cce7529044a732ef82e3827233ec01ae245b4ab39064b7fc3" ], + "merkleProofs" : { + "0" : [ "0x97cb598a18b30f14e7769ab0a48af96205eee47a865c21ed1a27eff79f173f00", "0xa6191a53104eedbcee8e72c8cdea7711bd0892700a46d740759f19ae4aa09c18" ], + "1" : [ "0xea11f4ef69b54777a682922371576f1109ac0007190892350c1f2b156bdfd047", "0xa6191a53104eedbcee8e72c8cdea7711bd0892700a46d740759f19ae4aa09c18" ], + "2" : [ "0x15c5ae7699bc260cce7529044a732ef82e3827233ec01ae245b4ab39064b7fc3", "0x78a4074ddbd4dda0362d2431f7629b0eb3dc490b4e83306f3e4ebe383d0ec907" ] + }, + "ids" : [ "0xa5ea2fcff9e3cf29d0c6f5f147b67ff24aec3bd4", "0x690a7fd66157e5b777256ea5214f634f61b3a72d", "0x41f547a71d1404f147606491f3b96e49f67d4658" ] +} diff --git a/consensus/common/src/test/resources/vector-slh-dsa-noble-2026-09-03.json b/consensus/common/src/test/resources/vector-slh-dsa-noble-2026-09-03.json new file mode 100755 index 0000000000..b5d70db943 --- /dev/null +++ b/consensus/common/src/test/resources/vector-slh-dsa-noble-2026-09-03.json @@ -0,0 +1,10 @@ +{ + "scheme": "slh-dsa-sha2-128s", + "form": "FIPS-205 external, empty context", + "source": "@noble/post-quantum 0.7.1", + "nota": "AERE-SINTETIC: the public key of a test vector generated with @noble/post-quantum for this cross-check; it belongs to no validator and no private key is in this file", + "pk": "77868c8946cf36281f0bcd8a5b299c4300d20feb42aa7a08893f402ce0c7b64a", + "msg": "616572652d6879627269642d63726f73732d766563746f722d323032362d3039", + "sig": "67fed7799e0b273a774c759571734072e3465b9b32becf6d334392143f72f49039f3f2f4e342a003333d1e1da2deacdffe2f52f3552f79487e0e0fd35e2e657152f6ab027523f4eec039a9b22d661efb90c4401d4a008693d862886ca038a1539fc8827667c85f81ce364b2dc12aea515786d606df7db8f9fcb7d1275eaa07051b481be40be4ee4905eb491c1509b2c755af8ec43d6bacbce519a39f19c6cce3de80a0488a3e724a33327ea3e85cbb9165e1395b744f64a637d79d287f17c4d61bb60b9e5bb03e88f73e19e0e81e3ea4a44d482d4f649756e3d0cdda173c372ed55470fab7e47c78fc7b189814ead0024cee40005b4d5b1a0e2729995c003e7c5c0843c47dc9f726ab8d0724377602e79fcf9f8b8e8cf52297251576079fe4d984f758e814c71e88352bf325239a913ffa8b14a3b94124d3ae14eb3c61326f8f1fcb8b7f70b1208cdc509a11a33d86c8d3284b760364f4095a333678ec856abcbdcd1f30867281e384bc4c9861aa82497d5b6bfd05c4c5ccef01d2d6d897aa9987f393916a2fec108e26108198f68693d26989bc93694ce191c00104ad626ef5c1555d38e08758d9211159aead1283e5276365f1e3c57df0d8f09526a8c6543b086f2a4d9ff6ef34333f2ffedb1a7a18f590b349eca3563008279563e6149bc8a87cd111b5f567636e0ba35ef93c6d97aec4b7e319aa9216eafc73336454161c35e31efb6d4c30a200b8186d8e683710ac01d330e3836f2594b5583b00f10ace8d607a3f3e53fef09d92c9ba46648acf78f72d74f6e9a7208b4eaa5a9e152b33c3f27ffa81c0401b399c33feccc21d373794973c12100443d59d71e76f37c8a688fb7c3c4f73a68ebeb204a45dde88d384ab2ef91974f8ef970f2c376107a05c7e4691522481e6ee6dbe478525444231e2bc53c530e652844c2342410214d401f00b5cbe01736c55f0a473434764d568d941e556deed1bd2678e646b41f5c7a6fa11a259849cda323dcf6e64dc64cf76e6734561bbc7dc0b49da4598fc79e2672e1aea7187c2d39a93e50052558e704fac8d963fc7cfbc424ceab9aabea9a151143ae2c25c945cec0d6a696b696ee4c8ce5c4f10248117f08771c002dac825c807adc5395af6ced8e8c687775137dc17319c0b235540f9dd7f0855aee37b1d6cd1b5033eeb824c0c6b25208827d1cd1616b829bcc266d4cab18a90a664c15d24f8f71cb4b744ac1f2aefcb72f9c1dad83cf48a6cf5f0f38857ce139c5d546310778b73488850c0c55354f5ac8649e715c5426fcab890f7c3be3c558e45adcd1bd44e37640a10519338a50f48fa394d9f6669a143821d03abffa7c442f36f09bb83811cbe4999e1cd2cf1276470d7a169e3900fd6e4399df2fc7e6c74d70af5cbe3169ee977f6a68bd29225b775f95cfa7a39bf513924910c194919f92f3e72a46e420f217648bf0492310f1bfeb5e41fb384a4d40988e83418e2e193745cfda92080d755e6d180b922df1eafb440c431d92dc8c6750f564df1ffbd4dbcefc6f1b7fbc3b931726852af11f18e58d4c998d47f985868bd74bc45159036704730db0b279222aab572ff762a6ffddad353cd9149118da44eb732d8fa7e462449ffdd0be5518b63abe26f360b837b5e97f41009a7adab94da2603fb3b7fbe902ca56d4f4a4cf6a2fc426dd75f37c620cf27f18187cc59fb23518cef5450ef0d6117ad5af77d7a436c58b90b9b1adb7aa8accc6f067b598ea2e2a12b1b7c4fca5331133bcdee7337450453548d704c4f35f1a9489413bed593fe90b57434fc22f51dff6de52db277dd83bd157de23106aa62531035ff84daa0dc71b330c84920ca14a922342437e0bdad4a3fa9e8c42639a759cbba77dd49b2a4e19a55cb01ddd7e2a680e37e2d844cf7e25c367009bb9596ed3dfb95d9dab8b6a8ea56979e776945f6ea4af3f4ae474d16c04acb97c8e003be87287d2b1d7a06f680d4419b537d67b20fbe377cbf8401f10eb7e709298a7f06c63b80e818f25b96c862b20327f228ee78178af3075280aad0b216cbca3b3d984c972a3df1984296368e9aeadae0f5604b0b556fa2e4e4df4ef812e5ada45562f3705ab82429506b0667e5015f8ff92b5a7e9f738c1045cce143657ecbad7b61122d7e8392012277e2f2ecb8b49ebb67f34c277004e085b4730d17eba1c1afbafe43f32ee1ea15a5e19613cae33f4dc2970ac31eac84f017622ce2c1ef1f1adf7882861a6f02cfa59c1c19301c628725030c0b43094a6968513152737e0af2f2fe1f1a27cf3a87ea7be403b12e7cfd26930fb0eee15f60048b62ceec6a322d7cc17b53e9bd2e93f22c78598f778eab003d9aeb2bd14ef38786bad3fba86875145b203a26229af48876916b1982cbac1c618d9e084d8eca8ecd0caad4315044126dafa9411af94ebc1f25d97cb27abc75d13eebee3b2186504957c24cbcf004e9b7d22de9dca8d215cb0c43818fe98a69a0e85d40e9892c459f498c8bcc3808724f3cfc8b0094e096cd67d6226c9aa3c4d3510cf16397881ad22fe6e2d9c87bae75cc40f8003b69f78f4676af2268dd982e982742a0ae85951c084ab14a9e34dc6ee509add0d8c6a0a2274135f0eb209652033912e15c28c9c291c30e33e4e2ee423208c66effaf2f4ee4d74774e0a6386c57304fedcf2e3e1044da478a3d33ccfe1cc9f925f2a1ffdba7d2dc30a7fdac96ba2f3c46b52dd8529844fcc8fce29a59aa6ed496b9b50725dddc63acdef7364d11dc6e1d3e04991df87859a304095eca35eb9dd8ad02b890863faae4edf085c8b9f9de3efd898d7e4ff8781121057e3dffd6abaf983266626b205f590903a32027357034d718fda15f8facbe82cfb8875c6c8b6649c2813f315d845e2d430ed0e9011f6493abc58fdbc2043cc592fbf2ed202a3c6202997a71d5ce4c6d746cb9219c32204e276309de853dd37e2eb4be2d27ab24970e2f87a1db15f132835735b66daf7a114428359e982294c82865fd7badeade0a2baf3c2c8f61ca3a22c83a0fa6c09f187a0cf5f70f05586e23ca5b7f648d20b9fc3215013d6b0005adb89589535d79b264acd4309b035c54ff87d38c22c02959d5110ebd80960a0dc2d2416d276278219690acef13f298a19b887b29af6a0cf38c30cecc7a73c64da71bda754ed54ef0cec5208604421fcfc3a79e20d871db9a2a60605cb98d081b965ad75580e5209b1ba9d94c2e626da3caa998e43c72a9998f7c0ba1c521ea3939723fa13cc1262ccf4628ff00ab2d778e99dc125a16c1d894b3cd4bba4bd02085458559a22632aeb9cb2e158332e8b6012a4415e231df0792fb931e2e2857210a3b1af79517b6fa55f6c7ca6dc1c056daec11fa22666c404b1b36cd7f46dea36ffe10ac71100986db369e71bb1c378f77e401eee32ae0e48965fd8b500ad3efac1effbd3c053ba210a2a58a25e7b9d5f0fdf79d58477455cfc0b2604492970885fba5ab2708b2e676acc61642889e958f23b5212e119894b95a9aafc904986db219160de187b0ef33f07f0b598c7a44dfe7c6508c403a91dbedad5dc26dae9bcd21903e4ea9754c42b3f01bb57cc3639b55d3aa40ca3b60591b26ebc5f1e225632d2a8f0a3b20ec1c13c8a80153f9c286f34c719feef85fa5f53ab42bb2f7dc4f02f3e706501bf3dac0d452a002a73d948d04a232439d56568c8d22991fd178ec94dfaca8f1fcc54d9be92d7acb25e27e45a9729b903c80cd94b806ade21db010693923bb45900c55bedc582ae27cdfe2b665dfe6bd6868976035dc85a9e85a7010e457736d00f7c685b0393d4daf163164d76f3004917f0b88417d32b1e79d4006cee735c4eac9fb3e6acb5432fd58495de8d314652fd286f36ccc424f7bffec59df9b9062ca6a38ab8adaec902f7d1b48b5ffab4b5384ed7be957bd2795bfe2a9caa72e69b9a4bcbd106ede1021a8221f47c7f41509c3e3d32fd1888379fd7cce5693ca33bd8810ac0a55a6a771c995d2fdcd2e94e84ce2ad1ab3e490ae9dedd2403335a9925f5f827922e3140dcf347f42f1fa6ac28b5e7afbde06fc100f95a1ca2f8aad4d1bbd789cfe0f0dc9ae8b1d453ef3253435539c9c01146fb60a93323e619e2f7fdfbcae40ccf84fb09633db41d284b2fefe7879a8aa57c8d3d0a96eb287ea865a4e9a15b8004401d6bb3139dd5ada82b2eaa0b44b16129568ef70e35844bf2bedd7cd9d3790b57e6bcbee06a2e9371045b2ea891a82fbae78e13464c6dcf3ea574328d265548f95bd507ee3c0cd5b3dcd3a5f344a334ebcc7892f6bf5f8a63110e148baf16344b09dbec02dca4f6ac5c86d6e1107646150a2edc785d798761f35551196bfb70dc7eaae2bb647197b946d7ba167d0f6e366fd633822fd1188b3c8bbc3a96da6d24d62ae5e85dd07551324c73a6a650c75ffadc0ad9737f4cb214ffe72ab6cf30f2a4aab57c25e5a99eb062e54d7e0aff93adbc2e4767a12796650c6f5b2db7463dbe4d5eef59ef13a0337cd38543d9d95bb66b26e6c8f55844fb4652582271206ef0250d38bbf795c5188a5a82755873871f0211e4f9207a257c5fcdc467913aa644cb9bb7eaca94b3ccd1a6191e94fef075aa482c1f11ab042dcbbad46c796241ab79c2f86bde9dfebddb270ebe7d8bbff3e614b98f54c152a54c7e75f68c8cd0855032e0c206ad25d7519ab3bef49e3fef664d9d0f0af5ea316c983d4a4013743b07203e039ce6e86b35e85d7ad383925cfa8ee6cc44fb5f1601e303657762515905b22ec2bdadaffd109f807667bb61b21162b6b2196a51b87303f8fd968a19426bf92e28d661c52efa2b58d0726539ba61813a3a14ec7f8deb5d13098cd03b1d7b545cfc91d1a2202c7548ca9f85d2ca888107ecbac63981d3c6084a7643d86c0ce773a0f023fad67605bd8606c0e5158879859488d9ba3c056c762fcf13695172205919c71e53097c3404f8566d9903bea68d39d5dd5c5ae04ec9afc682574f488a7b358f57906889fbfa02d451398f8bf433f2fdb0d9ddfe7a11c9fccdc58b9521ea6adaf31c78334decfd7f6a445dd5c08d71292d12c8f58d356cd0a4755a2f205bd1f833aacf31ef9374c30411a470af9d52707cedbf5d7c6677918b16ece8b44020a0ad0108d3d06f42c56492036a16f7ebc1ce146a4ff061c8f28a3393550be8d02e03a753a4de0e7a2c1b034eeeb7dcc25e36a1b3fe8be79032da57fd972c8d460c841315c55aaf47a1aa4118766bc20c5be078f8636779bca218a7b3c67eb231ec92712eb0583dab001d774fc41edbc59d29b66b63c72daa73c7da82e619ae92df376dc5048d484410a42270119a390255cac3bd48509201351cd0975933c402dc2878282108999bf9f0803ba69c1fab3c79dca5d1c48069b6609732dc5662c76a39d5a87bf0238275519bebee2a6843d7da2223e864cadc64e6bc35de75168eea1a4c76a956a5cc38075f019e4ddc9293e1674798913d0cb6b622ec2518bf384dfbee4f0e6530217bac4144bd3481173c37b5db7e25cae689eadeb5e35a40e1c4f33dda61b38bc066fe665e5375f04c5ffd282dd707a3ce8f29094075765de4d199800e38226dc95c91984afcc89a853126be1c747307ba73544b82c7b18b97e717bae81fa61fac001e2408ceac060a6c94bf40f3242bc9782b7f179ece763c73f21b269e7b79303b9c28b08278bab462e017995d9d4b488184af420292c8f08d92e630b34d6cafea52616f0f405934a16ed6515290869f1e853f60d5fa36453cd09c8604f8384be27992200095c3ea208a223de8930dd3248029ef196257cb40024da46188d925d3c0a19b3b01cd96310489b26262361320d8735838691dff538f54f685396fe780f1d3831311a3e77b57113a3a23f747aeb4ebed6aa7c1c16bd92766db04253282da0339fc49e61f1415dc788b8d31db4fc37a79283020029470eb5797fc50d5989f38bd9f46e35428e56c3622eeae2659d427a561493f9236bb197031bcf556825ee30ee98f4f271b8b9846633878b07e2792eeb4177d0ae2e77a6d812c7c8e8e2b7a88a0edcf759958e17789079c35cbe6bbd3114a5682bff4c005e642d3be071d9c07f00339f6bdf771d2d46d84a1712290eb71cd36223fc0fa3d64d6ab0b44aafdb96dab69c490c4e68acab6b228fa528efe7c41b09a93a2827ae0405cd011bf9f516bccf508c995588daf2cef1ea3fe792d21772fd8bab223d740d554fa7fbdfa0d7c2083b6442fc3ac849cd9dd0c13fe031e8b656a2849468b70745eea37fac1b0369959e93675048dc89f6029a378a014c5a65de94c7dedfad0ac0a2b2e6da90a25427fee681ec0e06824e32d9a85db98738ec651b60c71b45c22810d74fd93748c786bab42f8697fef5292ad518af07709dcca4ca2d677a187eeefdb3db719303c2cc3fd1f74e30f882fdbf44c89661306abd6431a22c0bbdf3d1b5f97799ebf29c05e62a5894c12538ea8de88567db9421d2a4b1c7faf0f039d0ace438a7165b989ae86e8c8699e3ce10710b0a04a09335dfebefc7217f3e0a919b57a1db05f81f7baf9443f2d71917eaeb52529c1491711b355caeb821d07238f7bea601109eceaf4e1503b4e0758826454b44357dc1269dc4f4757d8f5c8b2be605f7d7026017523d8b3580b72e43df61ecd5aec07baf3393a8efb18cd7333b1945da07e973b8c5bfcebb053d4b9c73efff86e3c1ae424d2cf3d48b38b6e345c74b8015677da947338276250c0a21db95d5c293966eba2e105acfb77f718845ed143693f6bf39974481393616ac8dce0e9014e07442e9726b3b5f258c6510842b394559e5a5763a19d0847e910afaf1a6dfc5b724bcc9c05c130ad37c1d967b1d07c81301dae7df61b5d62c0d1f7e24b6967cfb673681f1f8e91862f0b53c7b82096e37084772da8398c2f3cd0c704fa3519bcb2aeaf18193d1a8cc8c7ee285878dc665d2eecb857776bd8984a5796703a659effdfd67a1c7c758195f310baef4353804c197fe69b2c53d664e7722a1838c0719a462a4411262b3bec89fd5e6e3e4f1da598154bcd0fb12806903fc9656988f1f7866ddfe3d2d1224cd447422b91479ad69d48c9472f7a669850150f1129c88a8c09ee744169d6c550fb3742b2c225e4a01ce1a12e9affeade7e1df3b865a94b8522aaf79ce42628e3632f41a9d41da0e42b24dc80454a3d79f8f45f83a879acfce73658fe7d447d1f029e4966dccf4bedab315e6b7e265c698e969327042145b981c828eea5f81d6bfc3af770b0ea9ff412c640ce78ee297e0f22db7f71e701215920c7dde71d08060aba05a8c972bf804a4e743d3ad46cab8105ee6a6a718467fbe7a10db8032b611951ad158762436dd373d1d1c033d1b7997f9bd82ac08d12df4afd2003a9c8f40f3b29126e45c05dd325ee5d60f96cd73ef915f091beefa7ab35d39983b3438feb3f56f34bf86d258b04bad97f8b9c2299f1261b21bf2b7daa15a9f0b3c92c39569f71ea198a18115a4bcfd257d4ba98658ddd6a50623bad3f6a5bc1bc1bb665e4a1ea4ab99794515d1daea397420cd53cf2dd7c3002702d1345c2b8c861794bcc27df9e7468ede1608aba2c1504485cbc021698eb44ad5dbc4316ffc4de11edaacabb3904d036e02ed7a728cd29fd95b733bd2b0d0a3a7e0501f57bca4e74f8de6e412a9e21c88b209a9e3a32d3846d12e8db08f08b415e53eccca3cfac362a5da2437dcf9b5c9f89706155fb260bbda8cec5b386d1186daaa1c72f4940e844b3cc9fc7a60ab4e22d481790010ef36be8b8e025b7307d70e1ca3e11e755f4039a6d0ea55ebf86d309e6056d6e1b6732560026258d7b6e0b5ccebd59ab7ad5dd48dc41668b223d1e2d078aa7180176c33cf96bc466719b7fc8d444510e5fa3661e8ebc74d2b515a8d08a280b419ce245a2dad9ec876ffaa02d0474ecdfed995e52e880011a3d5febc5c520aadff075a4f9975eae6c3610e12719ff87d9586a029810dc7d8d4cf1f0ebfbaea18ee6b2ed19e387c18954451a943701d1f3376c9acefff7a841845af834acb99b54e8d14e341ce769242d6d6b558b42e63b62fa55c7ad7482175c81d11d700e1196ae0e3798b4e8893fd194fb0885a9d57c309b3b01a0c7ccc5f0594d12ec1c21eaf2af3ecf90cd5cd364a2edebea6d8dd3865c7cefe2ef66da644a630f955f472c859bff21ccc2ad778c8a6a1fed8428cef29d4aa13c6dc5edb7aef44fbcbd3374bf2ef5a439691929f04054ac5fdba0c564ca98502c5532fd1c8d2aba91788c71acc95f434b93dc4fa7b468a98465b70e766787ce5de56d7d539648aaf2671d8682eada24231e20c55ef7d2995674863ddca495ab5ff36ff2be4e18d20209098ae792e44348ce6138d22a407f1ba24b17b88aa470e889ae4c629a31014c97c8fd5a34042bb548dc90f98332962f61e7ba32cce11898807c263ee68eac042b71a7fd30faec8d462b05a9d9af742de2dde1226d70bb23303d58f0fc5bf072ad7d63cfda8e17be96ba9e319870084c916bc465b110422e5e64134ee5d2e6ab79122e1e39bd7413ce131ec915854e08716d4525849430b74953ed216eb880fb8422fec1c9edfe4da15fbc985ff7e7a4713a71cd219bf20c5b13da12d0e366d6b7542c72801562b383624d19d78bfaef312a0941e925b741460c7dd059e6536d1c94271a660f4311b0fda9a28fc6074bb942ff41f37cf30491db436695189b7aded4b9a7f8f9750eca3521fe64396cd8c0a2543c88d0646549c927c1e97b6663130376d33000542c5ce44b27d9e2602457339d598beed92b204c34645fb5a8e688d08cc32315d3e80b39639e86e6b002a2aae39a22b8eafa742d9cfa62245a60f8b8c72f9e55564b60ed4e557bf862668b49e521f9f61c4364cc9a253097fd1496121e63cb3ae444211fdd29a5d8a7ed2135aa993af4177fdeb424a6afaefe2171651f5aa25411c903bb15e91f829af927ec0feefa7ae7febff1b60c8b39fc55c3fa5baa526a8031a96641e896be98e6436146decb2ff20638f4d0dbb06e0cab890a7bdf9dd47af158ec621312624345041316e65c79e70d714bab7d0ae923772754081a0344f0cd36492c137804de093c2949f0c6e644c26105a6b490cd4f55944129a87c23c6c172be09eabc71d804ce76a8bb092ab05b3d5f563144d40bcc2ebdbbbeb3a185a2ac2c66500000456391574c613b36b23ab7021beaaa702803e01d51074e9f94838c37e4a9f48ffe6544f7f223b33924ed3f8bb096d3927174209d76cec40743ac7a7c45b5697081204b61b7d82136f5c84c97e01e66f98f9abb7e44dc2bb1513574bc2c0a8b96ee8f88520adab32c486f42d36dc62a00bb015fc6590b053295c161868cedb543465d72bd199539c81870bcee27c2063e20ada168c47c9b11f4d7ac2aaf62dcdad0b9b818ccf17d170abb8b0eee17205ba20c4ca128f98d092f4fa8f54a24defc3daaa2fcc9876ba502406664725589ce4392b1888d314a076b2e8bfc9f64cb2ac94b5c868dafc98bcfa36a38a77ea1d4a650b05b3e0c6be7906579f32dc04b8cf4e37f57e64cd0ee5248637c2a034904e9b20428745768e3ddb47dd7efb5e27b02a6008c3d57b3f5d99fb39526f8457e0debb5f170756a2ebbc2a66a5e1396ff2a92376e30a74e7acceafd4c92b04ceadb3c000af2118f2ef3c6ccf7e0e124a80c91916e5b225ea88aadcb4b3e36e62f2f3528d609a942fb08f555ed0c5e42614cf9fcf701d911d956db61449e175ddd1b0bba1b7ee300c7a34e54b42acccf6e7a3a47701a15b4082583dfec947a0ec13749c4435e8a1b44e808a0b8db4674e7ca34c8ce23c593a764a4882f42f0455799767e7a1d75a4419f9c429cc5dc6949cce4c70f90e19fc7942dbdf7f0e3a857c17fb336ae1779e738a1eabd7760546a215e7b419313cb1f8060e2552bf990c3123c6d2764f0b5dbdfeae498bb6f09bb7221b88da1f225c7a9f5a88c19d890a3740e0fdb31738ab32e12b16330fca9457eedd03ba61765f60f39536ef301f8b864486aa9a5b908d24a00abbe4364a1ffed84822906cf7edf72ccd16fc4112d309120af01f7c4b90a747a5536cf57bf94ce530df6e95873f06e42735581231f7f1f312a2bf14a325f1582a9f9dc5b11a77f166966e0cd90e86b511649fc293b50f28cdb4dda769b3ac7d190c7bbd470fe81383973e87a4cbdde34536fcf30ae9ea51c6ee907e6505311c0b4fc854aff34bd22b31c71a02f7296d950a13710e2d62161f97af62a5d92afe4c9c5ae69611c57acf16dd100b626b9230c78aabe2d500e87d427706ddfb5063426a2e8d6b521f671c73935c0d9961925c78111bcb0f421420dce466e5aeb380da9f037e9db535f50c2db0b27f0c77f9ccd0f145e7dc4cf28f600a2cd42728aa476b3c10f4d6580af0ac773410cc3faf34ea5a9a387a1fe90a7885c938f984dc543a1c4a6fcd6760538bde67588963eaa56305368097252a014c01db5ca15d8acdb4fc0b84b8599d82c78907b5eee0c6f07b2c22bb76ed072648ac9ff835f89704dd3f3d63fc32ea03513133e39f9c75e64957d1aa7c5fe7baf0a978e330ad62546c035a8e453e0fbf8f21990bea65f9d33852280f61224a79ff4c8a33138d50bd2bf44f5017ecdde038dd252484bb34be7596b35e76badbb11b2bccbd29869a00bd8efa2410864b661b347e3fa9e89404bc927c81c24c1a218a3d543a7b3b2453565bc55ecc94e86cfb46e28f698b7c47ef5e1d11e9c51d5e98b3936838c33b056dd377c75c661dbaa0a63c1eec52096477912821974ff06f9cbc499d0ab71922fd021ae460bdeaedf6f3cf5f260ca6cde4ca7fed26f99b59ab3113107c686c3e512a3f781078294ba870c911f05c0bde3d1931f62ecf44bfc73038220053db0cf0dcb5448f7e55addf7d65e03eccdddb647a2b28296070271d187a89fb108a03a4633a9c3c4a984034a609dba455116b6e299733dd80e707d8286bc47ef938ff78a64cede408c47efd6f3e4892d79fcf95de4fd284803341868b3af24235d9db7e1cf6455c5c1efe49a5d4eaa6f8f751a992b79595b78e68813e60ea8e72f875b2d82aad56f53281fe5f", + "selfVerify": true +} diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/Proposal.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/Proposal.java index 73bd09f3cf..64ece7de19 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/Proposal.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/Proposal.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-bounded-message-lists.patch. */ package org.hyperledger.besu.consensus.qbft.core.messagewrappers; import org.hyperledger.besu.consensus.common.bft.messagewrappers.BftMessage; import org.hyperledger.besu.consensus.common.bft.payload.SignedData; import org.hyperledger.besu.consensus.qbft.core.payload.PreparePayload; import org.hyperledger.besu.consensus.qbft.core.payload.ProposalPayload; import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; @@ -102,29 +108,29 @@ public class Proposal extends BftMessage { rlpOut.endList(); return rlpOut.encoded(); } /** * Decode. * * @param data the data * @param blockEncoder the qbft block encoder * @return the proposal */ public static Proposal decode(final Bytes data, final QbftBlockCodec blockEncoder) { final RLPInput rlpIn = RLP.input(data); rlpIn.enterList(); final SignedData payload = readPayload(rlpIn, rlpInput -> ProposalPayload.readFrom(rlpInput, blockEncoder)); rlpIn.enterList(); final List> roundChanges = - rlpIn.readList(r -> readPayload(r, RoundChangePayload::readFrom)); + rlpIn.readList(r -> readPayload(r, RoundChangePayload::readFrom), MAX_LIST_ENTRIES); final List> prepares = - rlpIn.readList(r -> readPayload(r, PreparePayload::readFrom)); + rlpIn.readList(r -> readPayload(r, PreparePayload::readFrom), MAX_LIST_ENTRIES); rlpIn.leaveList(); rlpIn.leaveList(); return new Proposal(payload, roundChanges, prepares); } } diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/RoundChange.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/RoundChange.java index 6c922b101f..5d7cfce849 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/RoundChange.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/RoundChange.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-bounded-message-lists.patch. */ package org.hyperledger.besu.consensus.qbft.core.messagewrappers; import org.hyperledger.besu.consensus.common.bft.messagewrappers.BftMessage; import org.hyperledger.besu.consensus.common.bft.payload.SignedData; import org.hyperledger.besu.consensus.qbft.core.payload.PreparePayload; import org.hyperledger.besu.consensus.qbft.core.payload.PreparedRoundMetadata; import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; import org.hyperledger.besu.ethereum.core.encoding.BlockAccessListDecoder; import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; @@ -126,37 +132,37 @@ public class RoundChange extends BftMessage { * @param blockEncoder the qbft block encoder * @return the round change */ public static RoundChange decode(final Bytes data, final QbftBlockCodec blockEncoder) { final RLPInput rlpIn = RLP.input(data); rlpIn.enterList(); final SignedData payload = readPayload(rlpIn, RoundChangePayload::readFrom); final Optional block; if (rlpIn.nextIsList() && rlpIn.nextSize() == 0) { rlpIn.skipNext(); block = Optional.empty(); } else { block = Optional.of(blockEncoder.readFrom(rlpIn)); } final Optional blockAccessList = readBlockAccessList(rlpIn); final List> prepares = - rlpIn.readList(r -> readPayload(r, PreparePayload::readFrom)); + rlpIn.readList(r -> readPayload(r, PreparePayload::readFrom), MAX_LIST_ENTRIES); rlpIn.leaveList(); return new RoundChange(payload, block, blockAccessList, blockEncoder, prepares); } private static Optional readBlockAccessList(final RLPInput rlpIn) { if (rlpIn.isEndOfCurrentList()) { return Optional.empty(); } if (!rlpIn.nextIsNull()) { return Optional.of(BlockAccessListDecoder.decode(rlpIn)); } rlpIn.skipNext(); return Optional.empty(); } } diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/network/QbftMessageTransmitter.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/network/QbftMessageTransmitter.java index 18437e30fd..236b6cb35b 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/network/QbftMessageTransmitter.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/network/QbftMessageTransmitter.java @@ -125,44 +125,44 @@ public class QbftMessageTransmitter { multicaster.send(message); } catch (final SecurityModuleException e) { LOG.warn("Failed to generate signature for Proposal (not sent): {} ", e.getMessage()); } } /** * Multicast prepare. * * @param roundIdentifier the round identifier * @param digest the digest */ public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { multicastPrepare(roundIdentifier, digest, Optional.empty()); } /** * Multicast a prepare carrying an OPTIONAL post-quantum seal of this node. * - *

    Sigiliul vine GATA CALCULAT de la apelant, si asta nu e comoditate: semnaturile Falcon sunt + *

    The seal arrives ALREADY COMPUTED from the caller, and that is not a convenience: Falcon signatures are * randomized, so signing the same message twice yields two different byte strings. If the local * copy and the one on the wire each signed their own, the same validator would produce two - * valide si DIFERITE pentru aceeasi runda. Se calculeaza o data, sus, si se trece prin amandoua. + * valid and DIFFERENT for the same round. It is computed once, above, and passed through both. * * @param roundIdentifier the round identifier * @param digest the digest * @param falconSeal the seal, or empty */ public void multicastPrepare( final ConsensusRoundIdentifier roundIdentifier, final Hash digest, final Optional falconSeal) { try { final Prepare data = messageFactory.createPrepare(roundIdentifier, digest, falconSeal); final PrepareMessageData message = PrepareMessageData.create(data); multicaster.send(message); } catch (final SecurityModuleException e) { LOG.warn("Failed to generate signature for Prepare (not sent): {} ", e.getMessage()); } } diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/CommitPayload.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/CommitPayload.java index 8b788f499e..cdfaaf24c6 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/CommitPayload.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/CommitPayload.java @@ -5,73 +5,76 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft.core.payload; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqSealed; import org.hyperledger.besu.consensus.common.bft.PqAnchorV2; import org.hyperledger.besu.consensus.common.bft.SchemeSeal; import org.hyperledger.besu.consensus.common.bft.SealSchemes; import org.hyperledger.besu.consensus.common.bft.payload.Payload; import org.hyperledger.besu.consensus.qbft.core.messagedata.QbftV1; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; import org.apache.tuweni.bytes.Bytes; /** * The Commit payload. * *

    AERE hybrid PQC extension: a Commit payload MAY carry an OPTIONAL parallel Falcon-512 seal, so * that every validator's post-quantum seal over the commit hash is GOSSIPED alongside the decisive * ECDSA commit seal on the same message. The Falcon seal is appended only when present, so a * Falcon-less Commit encodes byte-for-byte identically to upstream Besu. When present, the whole * payload (including the Falcon seal) is covered by the author's ECDSA signature, binding the * claimed Falcon validator index to the sender's identity. * *

    AERE FIX-MALEABILITATE: this payload has exactly ONE valid encoding for a given value. See * {@link #readFrom(RLPInput)}. */ -public class CommitPayload extends QbftPayload { +public class CommitPayload extends QbftPayload implements PqSealed { private static final int TYPE = QbftV1.COMMIT; private final ConsensusRoundIdentifier roundIdentifier; private final Hash digest; private final SECPSignature commitSeal; private final Optional falconSeal; // AERE HIBRID (2026-08-25): the NON-Falcon scheme seals of a hybrid certificate. Falcon keeps // living in the legacy slot above, so one signature has exactly one home and the wire format of // a Falcon-only commit is untouched. Empty on every commit the live fleet emits today. private final List extraSeals; /** * Instantiates a new Commit payload (no Falcon seal). * * @param roundIdentifier the round identifier * @param digest the digest * @param commitSeal the commit seal */ public CommitPayload( final ConsensusRoundIdentifier roundIdentifier, final Hash digest, @@ -306,44 +309,55 @@ public class CommitPayload extends QbftPayload { * @return the digest */ public Hash getDigest() { return digest; } /** * Gets commit seal. * * @return the commit seal */ public SECPSignature getCommitSeal() { return commitSeal; } /** * Gets the optional parallel post-quantum Falcon seal. * * @return the Falcon seal if this commit carried one, otherwise empty */ + @Override public Optional getFalconSeal() { return falconSeal; } + /** AIP-22 stage 4: the commit seal's signer, recovered as CommitValidator recovers it (empty when it does not recover). */ + @Override + public Optional

    commitSealSigner() { + try { + return Optional.ofNullable(Util.signatureToAddress(commitSeal, digest)); + } catch (final RuntimeException e) { + return Optional.empty(); + } + } + /** * Gets the non-Falcon scheme seals of a hybrid certificate. * * @return the extra seals, empty for every commit the live fleet emits today */ public List getExtraSeals() { return extraSeals; } @Override public ConsensusRoundIdentifier getRoundIdentifier() { return roundIdentifier; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/MessageFactory.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/MessageFactory.java index 11426d6a68..24777b59b7 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/MessageFactory.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/MessageFactory.java @@ -125,44 +125,44 @@ public class MessageFactory { final QbftBlock block, final List> roundChanges, final List> prepares) { return createProposal(roundIdentifier, block, Optional.empty(), roundChanges, prepares); } /** * Create Prepare payload. * * @param roundIdentifier the round identifier * @param digest the digest * @return the prepare */ public Prepare createPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { return createPrepare(roundIdentifier, digest, Optional.empty()); } /** * Create a Prepare carrying an OPTIONAL post-quantum seal of its author. * - *

    AERE PQ (2026-08-28), pasul 1: firul poate purta sigiliul, si nimic nu il emite inca - - * fiecare apel de azi trece prin varianta fara sigiliu de mai sus. Ca la commit, semnatura ECDSA - * a autorului acopera INTREG payload-ul, deci si sigiliul, ceea ce leaga indexul revendicat de - * identitatea celui care trimite mesajul. + *

    AERE PQ (2026-08-28), step 1: the wire can carry the seal, and nothing emits it yet - + * every call today goes through the seal-less variant above. As with commit, the author's ECDSA + * signature covers the WHOLE payload, and so the seal too, which binds the claimed index to + * the identity of whoever sends the message. * * @param roundIdentifier the round identifier * @param digest the digest * @param falconSeal the author's post-quantum seal, or empty * @return the prepare */ public Prepare createPrepare( final ConsensusRoundIdentifier roundIdentifier, final Hash digest, final Optional falconSeal) { final PreparePayload payload = new PreparePayload(roundIdentifier, digest, falconSeal); return new Prepare(createSignedMessage(payload)); } /** * Create commit payload. * * @param roundIdentifier the round identifier * @param digest the digest * @param commitSeal the commit seal diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/PreparePayload.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/PreparePayload.java index 727cbccc81..dea9b70233 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/PreparePayload.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/PreparePayload.java @@ -5,73 +5,76 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft.core.payload; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqAnchor; +import org.hyperledger.besu.consensus.common.bft.PqSealed; import org.hyperledger.besu.consensus.common.bft.payload.Payload; import org.hyperledger.besu.consensus.qbft.core.messagedata.QbftV1; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; /** * The Prepare payload. * *

    AERE PQ (2026-08-28), step 1 of PREPARE-SI-ROUNDCHANGE-SUB-PQ-PROIECTARE-2026-08-28: a PREPARE * MAY carry an OPTIONAL Falcon-512 seal from its author, appended at the end, exactly as * {@code CommitPayload} does. A PREPARE without a seal encodes byte for byte as upstream, * and that is precisely the property that lets the binary be rolled onto a live fleet without a * flag day. * *

    NOTHING EMITS SUCH A PREPARE YET. This file only makes the wire capable of carrying one * and of refusing a malformed one. Emission is the next step and has its own gate, following the * rule paid for at commit: first the binary everywhere, then emission, and only much later * enforcement. * *

    What the seal signs is NOT this file's business, and the design note states it: its own * domain {@code AERE-PQ-PREPARE-1} over (chainId, number, ROUND, digest). If it signed the same * bytes as a commit seal, a PREPARE seal given honestly could be pasted onto a forged COMMIT and * the enforcement there would accept it. */ -public class PreparePayload extends QbftPayload { +public class PreparePayload extends QbftPayload implements PqSealed { private static final int TYPE = QbftV1.PREPARE; private final ConsensusRoundIdentifier roundIdentifier; private final Hash digest; private final Optional falconSeal; /** * Instantiates a new Prepare payload, without a post-quantum seal. Encodes byte-for-byte as * upstream Besu. * * @param roundIdentifier the round identifier * @param digest the digest */ public PreparePayload(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { this(roundIdentifier, digest, Optional.empty()); } /** * Instantiates a new Prepare payload carrying an optional Falcon-512 seal of its author. * * @param roundIdentifier the round identifier @@ -154,44 +157,56 @@ public class PreparePayload extends QbftPayload { @Override public int getMessageType() { return TYPE; } /** * Gets digest. * * @return the digest */ public Hash getDigest() { return digest; } /** * The author's post-quantum seal, when the message carries one. * * @return the seal, or empty */ + @Override public Optional getFalconSeal() { return falconSeal; } + /** AIP-22 stage 4: the PREPARE seal's message, as PqPrepareEnforcement builds it. */ + @Override + public Optional pqSealMessage(final long chainId) { + return Optional.of( + PqAnchor.prepareMessage( + chainId, + roundIdentifier.getSequenceNumber(), + roundIdentifier.getRoundNumber(), + digest.getBytes())); + } + @Override public ConsensusRoundIdentifier getRoundIdentifier() { return roundIdentifier; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final PreparePayload that = (PreparePayload) o; return Objects.equals(roundIdentifier, that.roundIdentifier) && Objects.equals(digest, that.digest) && Objects.equals(falconSeal, that.falconSeal); } @Override diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/ProposalPayload.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/ProposalPayload.java index 813e798a97..ada90e3cd9 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/ProposalPayload.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/ProposalPayload.java @@ -5,82 +5,85 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft.core.payload; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqAnchor; +import org.hyperledger.besu.consensus.common.bft.PqSealed; import org.hyperledger.besu.consensus.qbft.core.messagedata.QbftV1; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; import org.hyperledger.besu.ethereum.core.encoding.BlockAccessListDecoder; import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import java.util.Objects; import java.util.Optional; import com.google.common.base.MoreObjects; +import org.apache.tuweni.bytes.Bytes32; /** * The Proposal payload. * *

    AERE PQ (2026-08-30), the hot-path step after PREPARE: a PROPOSAL MAY carry an OPTIONAL * Falcon-512 seal from its proposer, appended after the block-access-list element, exactly as * {@code PreparePayload} appends its seal. A proposal without a seal encodes byte for byte as * upstream, which is the property that lets the binary be rolled onto a live fleet without a * flag day. * *

    NOTHING EMITS SUCH A PROPOSAL YET. This file only makes the wire capable of carrying * one and of refusing a malformed one. Emission has its own gate * ({@code aere.pq.proposalPq.attachBlock}); enforcement has its own height * ({@code aere.pq.proposalPq.forkBlock}); both are absent on every node today, and absent means * never. * *

    What the seal signs is NOT this file's business: its own domain * {@code AERE-PQ-PROPOSAL-1} over (chainId, height, ROUND, digest) - see * {@code PqAnchor.proposalMessage}. A proposal is an offer, not a vote: with the commit or PREPARE * domain, a proposal seal given honestly could be pasted onto a forged vote and counted. * *

    Why the trailing element is parsed strictly: the payload is AUTHENTICATED - the author * is recovered from the signature over the encoded payload. A decoder that silently ignored an * unknown trailing element would re-encode without it, so two different byte strings would * authenticate to the same proposer. When a fifth element is present it must be exactly a seal, * and it must be the last element; otherwise the message is refused. Messages of the upstream * shapes (three or four elements) are read exactly as upstream reads them. */ -public class ProposalPayload extends QbftPayload { +public class ProposalPayload extends QbftPayload implements PqSealed { private static final int TYPE = QbftV1.PROPOSAL; private final ConsensusRoundIdentifier roundIdentifier; private final QbftBlock proposedBlock; private final QbftBlockCodec blockEncoder; private final Optional blockAccessList; private final Optional falconSeal; /** * Instantiates a new Proposal payload carrying an optional post-quantum seal of its proposer. * * @param roundIdentifier the round identifier * @param proposedBlock the proposed block * @param blockEncoder the qbft block encoder * @param blockAccessList the block access list * @param falconSeal the proposer's post-quantum seal, or empty */ public ProposalPayload( final ConsensusRoundIdentifier roundIdentifier, final QbftBlock proposedBlock, @@ -185,44 +188,56 @@ public class ProposalPayload extends QbftPayload { * @return the proposed block */ public QbftBlock getProposedBlock() { return proposedBlock; } /** * Gets block access list. * * @return the block access list */ public Optional getBlockAccessList() { return blockAccessList; } /** * The proposer's post-quantum seal, when the message carries one. * * @return the seal, or empty */ + @Override public Optional getFalconSeal() { return falconSeal; } + /** AIP-22 stage 4: the PROPOSAL seal's message over the proposed block's hash, as PqProposalEnforcement builds it. */ + @Override + public Optional pqSealMessage(final long chainId) { + return Optional.of( + PqAnchor.proposalMessage( + chainId, + roundIdentifier.getSequenceNumber(), + roundIdentifier.getRoundNumber(), + proposedBlock.getHash().getBytes())); + } + @Override public int getMessageType() { return TYPE; } @Override public ConsensusRoundIdentifier getRoundIdentifier() { return roundIdentifier; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ProposalPayload that = (ProposalPayload) o; return Objects.equals(roundIdentifier, that.roundIdentifier) diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java index 8804b6a46c..736b9e7299 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/payload/RoundChangePayload.java @@ -5,80 +5,83 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft.core.payload; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqAnchor; +import org.hyperledger.besu.consensus.common.bft.PqSealed; import org.hyperledger.besu.consensus.qbft.core.messagedata.QbftV1; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import java.util.Objects; import java.util.Optional; import com.google.common.base.MoreObjects; +import org.apache.tuweni.bytes.Bytes32; /** * The Round change payload. * *

    AERE PQ (2026-08-31), the LAST hot-path message: a ROUND-CHANGE MAY carry an OPTIONAL * Falcon-512 seal from its author, appended after the prepared-metadata list, exactly as {@code * ProposalPayload} appends its seal. A round-change without a seal encodes byte for byte as * upstream, which is the property that lets the binary be rolled onto a live fleet without a * flag day. * *

    NOTHING EMITS SUCH A ROUND-CHANGE YET. This file only makes the wire capable of * carrying one and of refusing a malformed one. Emission has its own gate * ({@code aere.pq.roundChangePq.attachBlock}); enforcement has its own height * ({@code aere.pq.roundChangePq.forkBlock}); both are absent on every node today, and absent means * never. * *

    What the seal signs is NOT this file's business: its own domain * {@code AERE-PQ-ROUNDCHANGE-1} over (chainId, height, targetRound, prepared metadata) - see * {@code PqAnchor.roundChangeMessage}. A round-change STEERS rounds: a quorum of them opens a new * round, and one claiming a prepared block decides which block gets re-proposed. Its seal must be * transferable neither onto a vote nor onto a proposal, nor between a bare round-change and one * with metadata. * *

    Why the trailing element is parsed strictly: the payload is AUTHENTICATED - the author * is recovered from the signature over the encoded payload. A decoder that silently ignored an * unknown trailing element would re-encode without it, so two different byte strings would * authenticate to the same author. When a fourth element is present it must be exactly a seal, and * it must be the last element; otherwise the message is refused. Messages of the upstream shape * (three elements) are read exactly as upstream reads them. */ -public class RoundChangePayload extends QbftPayload { +public class RoundChangePayload extends QbftPayload implements PqSealed { private static final int TYPE = QbftV1.ROUND_CHANGE; private final ConsensusRoundIdentifier roundChangeIdentifier; private final Optional preparedRoundMetadata; private final Optional falconSeal; /** * Instantiates a new Round change payload carrying an optional post-quantum seal of its author. * * @param roundChangeIdentifier the round change identifier * @param preparedRoundMetadata the prepared round metadata * @param falconSeal the author's post-quantum seal, or empty */ public RoundChangePayload( final ConsensusRoundIdentifier roundChangeIdentifier, final Optional preparedRoundMetadata, final Optional falconSeal) { this.roundChangeIdentifier = roundChangeIdentifier; this.preparedRoundMetadata = preparedRoundMetadata; this.falconSeal = falconSeal == null ? Optional.empty() : falconSeal; } @@ -98,44 +101,61 @@ public class RoundChangePayload extends QbftPayload { @Override public ConsensusRoundIdentifier getRoundIdentifier() { return roundChangeIdentifier; } /** * Gets prepared round metadata. * * @return the prepared round metadata */ public Optional getPreparedRoundMetadata() { return preparedRoundMetadata; } /** * The author's post-quantum seal, when the message carries one. * * @return the seal, or empty */ + @Override public Optional getFalconSeal() { return falconSeal; } + /** AIP-22 stage 4: the ROUND-CHANGE seal's message, prepared or not, as PqRoundChangeEnforcement builds it. */ + @Override + public Optional pqSealMessage(final long chainId) { + final long height = roundChangeIdentifier.getSequenceNumber(); + final int targetRound = roundChangeIdentifier.getRoundNumber(); + return Optional.of( + preparedRoundMetadata.isPresent() + ? PqAnchor.roundChangeMessage( + chainId, + height, + targetRound, + preparedRoundMetadata.get().getPreparedRound(), + preparedRoundMetadata.get().getPreparedBlockHash().getBytes()) + : PqAnchor.roundChangeMessage(chainId, height, targetRound)); + } + @Override public void writeTo(final RLPOutput rlpOutput) { // RLP encode of the message data content (round identifier and prepared certificate) rlpOutput.startList(); writeConsensusRound(rlpOutput); rlpOutput.startList(); preparedRoundMetadata.ifPresent(prm -> prm.writeTo(rlpOutput)); rlpOutput.endList(); // The seal is written only when present, so a seal-less round-change is identical to upstream. if (falconSeal.isPresent()) { final FalconSeal fs = falconSeal.get(); rlpOutput.startList(); rlpOutput.writeIntScalar(fs.getValidatorIndex()); rlpOutput.writeBytes(fs.getSignature()); rlpOutput.endList(); } rlpOutput.endList(); diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java index 3d0138d9c5..8dc438b412 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRound.java @@ -5,40 +5,41 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft.core.statemachine; 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.FalconSeal; import org.hyperledger.besu.consensus.common.bft.FalconSealSupport; import org.hyperledger.besu.consensus.common.bft.PqAnchor; import org.hyperledger.besu.consensus.common.bft.PqAnchorNotReadyException; import org.hyperledger.besu.consensus.common.bft.PqSealCache; import org.hyperledger.besu.consensus.common.bft.RoundTimer; import org.hyperledger.besu.consensus.common.bft.blockcreation.PqAnchorProducer; import org.hyperledger.besu.consensus.common.bft.payload.SignedData; import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Commit; import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Prepare; import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Proposal; import org.hyperledger.besu.consensus.qbft.core.network.QbftMessageTransmitter; import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory; import org.hyperledger.besu.consensus.qbft.core.payload.PreparePayload; import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreator; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreator.BlockCreationResult; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockHeader; @@ -87,40 +88,60 @@ public class QbftRound { private final QbftMessageTransmitter transmitter; private final QbftBlockHeader parentHeader; // AERE hybrid PQC: guards against re-importing once a block has been successfully imported this // round, while still allowing a retry if an earlier import attempt was rejected (e.g. a post-fork // Falcon quorum certificate that was not yet complete when the first attempt ran). private boolean blockImported = false; // AERE ANCHOR V2: the ROUND-INDEPENDENT on-chain hash of the block this round is deciding. // // Why it is not simply block.getHash(): a proposal block is built with the COMMITTED-SEAL header // functions, so its hash includes the round number and differs between rounds for one and the // same block. The certificate carried by the NEXT block is looked up, and its message M is built, // from the parent's on-chain hash, which excludes both the commit seals and the round. Keying the // seal cache on anything round dependent would silently produce empty certificates at every round // change. Replacing the round with 0 in the committed-seal encoding yields byte-for-byte the // on-chain pre-image, so this is the same hash the parent header will carry once imported. private Hash pqOnchainHash; + // AERE D-337 (2026-09-28): the post-quantum seals of THIS node's Commit in this round, signed ONCE. + // + // Two paths emit this node's Commit: accepting the proposal (the local copy, and the wire copy in + // the rare case the node is already prepared) and becoming prepared later (the wire copy, the + // usual case). Each used to sign on its own, so at the parent of an anchor every Besu validator + // made two SLH-DSA signatures back to back on the consensus thread, the first before its Prepare + // and the second between "prepared" and its Commit. Measured on 2800 (2026-09-28, import times in + // ms measured at a reading node of the network): the anchor-parent slot 1,036 ms against + // 599 for the other slots. The message is fixed by (block, commit hash), both fixed for the round + // once the proposal is accepted, so the second signing could only produce another valid seal over + // the same bytes. It now reuses the first, and the local and wire Commits carry the same seals + // (Falcon is randomised: before this, they carried two different valid Falcon seals). + // + // What is NOT reused: an attempt that produced no Falcon seal, or no extras where extras apply. + // Those are retried exactly as before, so a node whose first attempt failed keeps its second chance. + private Hash pqSealedCommitHash; + private Optional pqSealedFalcon = Optional.empty(); + private java.util.List pqSealedExtras = + java.util.List.of(); + /** * Instantiates a new Qbft round. * * @param roundState the round state * @param blockCreator the block creator * @param blockInterface the block interface * @param protocolSchedule the protocol schedule * @param observers the observers * @param nodeKey the node key * @param localAddress the local address * @param messageFactory the message factory * @param transmitter the transmitter * @param roundTimer the round timer * @param parentHeader the parent header */ public QbftRound( final RoundState roundState, final QbftBlockCreator blockCreator, final QbftBlockInterface blockInterface, final QbftProtocolSchedule protocolSchedule, @@ -411,44 +432,46 @@ public class QbftRound { private boolean updateStateWithProposedBlock(final Proposal msg) { final boolean wasPrepared = roundState.isPrepared(); final boolean blockAccepted = roundState.setProposedBlock(msg); if (blockAccepted) { final QbftBlock block = roundState.getProposedBlock().get(); // AERE hybrid PQC: compute the commit hash once, then produce BOTH the decisive ECDSA commit // seal and the parallel Falcon-512 seal over the SAME commit hash. The Falcon seal is gossiped // on the commit message so every validator's post-quantum seal reaches the block assembler. final Hash commitHash; final SECPSignature commitSeal; try { commitHash = commitHashFor(block); commitSeal = nodeKey.sign(Bytes32.wrap(commitHash.getBytes())); } catch (final SecurityModuleException e) { LOG.warn("Failed to construct commit seal; {}", e.getMessage()); return true; } - final Optional falconSeal = falconSealFor(block, commitHash); - // AERE HYBRID: the other schemes' extras, over the SAME message; empty on any node today. + // AERE HYBRID: the Falcon seal and the other schemes' extras, over the SAME message, signed + // once per round (D-337, pqSealCommitOnce). + pqSealCommitOnce(block, commitHash); + final Optional falconSeal = pqSealedFalcon; final java.util.List extraSeals = - falconSeal.isPresent() ? extraSealsFor(block, commitHash) : java.util.List.of(); + pqSealedExtras; // There are times handling a proposed block is enough to enter prepared. if (wasPrepared != roundState.isPrepared()) { LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); if (!extraSeals.isEmpty()) { transmitter.multicastCommit( getRoundIdentifier(), block.getHash(), commitSeal, falconSeal, extraSeals); } else if (falconSeal.isPresent()) { transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), commitSeal, falconSeal); } else { transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), commitSeal); } } // can automatically add _our_ commit message to the roundState // cannot create a prepare message here, as it may be _our_ proposal, and thus we cannot also // prepare try { final Commit localCommitMessage = falconSeal.isPresent() @@ -469,44 +492,46 @@ public class QbftRound { // AERE ANCHOR V2: remember every Falcon seal heard for THIS block, so the proposer of the // NEXT block can carry a certificate over it. This is the local node's own seal. pqCacheHeardSeals(block); // It is possible sufficient commit seals are now available and the block should be imported maybeImportBlock(); } return blockAccepted; } private void peerIsPrepared(final Prepare msg) { final boolean wasPrepared = roundState.isPrepared(); roundState.addPrepareMessage(msg); if (wasPrepared != roundState.isPrepared()) { LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); final QbftBlock block = roundState.getProposedBlock().get(); try { final Hash commitHash = commitHashFor(block); final SECPSignature commitSeal = nodeKey.sign(Bytes32.wrap(commitHash.getBytes())); - final Optional falconSeal = falconSealFor(block, commitHash); - // AERE HIBRID: aceleasi extrase si pe drumul tarziu, ca cele doua locuri sa nu divearga. + // AERE HIBRID: aceleasi sigilii si pe drumul tarziu, ca cele doua locuri sa nu divearga; D-337: + // chiar ACELEASI, semnate o data la acceptarea propunerii, nu o a doua semnare SLH-DSA aici. + pqSealCommitOnce(block, commitHash); + final Optional falconSeal = pqSealedFalcon; final java.util.List extraSeals = - falconSeal.isPresent() ? extraSealsFor(block, commitHash) : java.util.List.of(); + pqSealedExtras; if (!extraSeals.isEmpty()) { transmitter.multicastCommit( getRoundIdentifier(), block.getHash(), commitSeal, falconSeal, extraSeals); } else if (falconSeal.isPresent()) { transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), commitSeal, falconSeal); } else { transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), commitSeal); } // Note: the local-node's commit message was added to RoundState on block acceptance // and thus does not need to be done again here. } catch (final SecurityModuleException e) { LOG.warn("Failed to construct a commit seal: {}", e.getMessage()); } } } private void peerIsCommitted(final Commit msg) { roundState.addCommitMessage(msg); // AERE ANCHOR V2: top up the seal cache on EVERY commit, including commits that arrive after // this node has already imported the block. Those late seals are exactly the ones that let a @@ -522,53 +547,79 @@ public class QbftRound { maybeImportBlock(); } private void maybeImportBlock() { if (roundState.isCommitted() && !blockImported) { importBlockToChain(); } } private void importBlockToChain() { // AERE hybrid PQC: pass the gossiped Falcon seals collected from commit messages so the block // assembler can embed the post-quantum certificate. When no Falcon seals were gossiped (Falcon // disabled), fall back to the unchanged ECDSA-only sealing path. // // WHAT GETS EMBEDDED, corrected 2026-08-19 (finding D-235): NOT a per-block 2f+1 quorum. The // assembler writes a certificate only at an anchor height, over the PARENT, and it needs at // 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 // 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 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 falconSeals = roundState.getFalconSeals(); final QbftBlock blockToImport = falconSeals.isEmpty() ? blockCreator.createSealedBlock( roundState.getProposedBlock().get(), roundState.getRoundIdentifier().getRoundNumber(), roundState.getCommitSeals()) : blockCreator.createSealedBlock( roundState.getProposedBlock().get(), roundState.getRoundIdentifier().getRoundNumber(), roundState.getCommitSeals(), 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(); if (getRoundIdentifier().getRoundNumber() > 0) { LOG.info( "Importing proposed block to chain. round={}, hash={}", getRoundIdentifier(), blockToImport.getHash()); } else { LOG.debug( "Importing proposed block to chain. round={}, hash={}", getRoundIdentifier(), blockToImport.getHash()); } final QbftBlockImporter blockImporter = protocolSchedule.getBlockImporter(blockToImport.getHeader()); final boolean result = blockImporter.importBlock(blockToImport, roundState.getProposedBlockAccessList()); if (!result) { // Do NOT set blockImported: a post-fork block whose Falcon quorum certificate is not yet // complete can be retried as more commits (with valid Falcon seals) arrive this round. @@ -643,93 +694,122 @@ public class QbftRound { /** * AERE ANCHOR V2: the round-independent on-chain hash of the block this round is deciding, * computed once per round. Replacing the round with 0 in the committed-seal encoding produces * byte-for-byte the on-chain pre-image, because that encoding differs from the on-chain one only * by writing the real round instead of a forced zero. */ private Hash pqOnchainHashOf(final QbftBlock block) { if (pqOnchainHash == null) { pqOnchainHash = blockInterface.replaceRoundForCommitBlock(block, 0).getHash(); } return pqOnchainHash; } /** * AERE HIBRID (2026-08-25): the non-Falcon seals of this node's hybrid certificate, over the * SAME message the Falcon seal signs (the two are one certificate; two messages would be two * certificates and the verifier could not bind them). Empty on every node that is not * hybrid-configured, and below the emission gate: {@link HybridSealProducer} never throws and * never emits half a certificate. */ - private java.util.List extraSealsFor( + java.util.List extraSealsFor( final QbftBlock block, final Hash commitHash) { // ANCHOR PARENTS ONLY (2026-09-03, D-329): an SLH-DSA-128s signature costs seconds here; it is // only ever used in the next anchor's certificate, which is built over the anchor's parent. So // the extras are signed only when the block being committed is the parent of an anchor height. // The same predicate gates the enforcement (PqCommitEnforcement.anchorParentByConfig). final long number = block.getHeader().getNumber(); - if (!org.hyperledger.besu.consensus.common.bft.blockcreation.PqAnchorProducer.config() - .anchorAppliesAt(number + 1L)) { + if (!pqExtrasApplyAt(number)) { return java.util.List.of(); } return hybridExtrasOrEmpty(number, pqSealMessageFor(block, commitHash)); } + /** The anchor-parent predicate of {@link #extraSealsFor}, in one place for it and the D-337 reuse. */ + static boolean pqExtrasApplyAt(final long number) { + return org.hyperledger.besu.consensus.common.bft.blockcreation.PqAnchorProducer.config() + .anchorAppliesAt(number + 1L); + } + + /** + * D-337 (2026-09-28): signs this node's Commit seals for this round at most once. A result is + * reused only when it is complete - a Falcon seal, and the extras wherever extras apply - so an + * attempt that failed is retried on the next path exactly as before this change. + * + *

    Package-private, with {@link #falconSealFor} and {@link #extraSealsFor}, for the probe that + * counts the signings on the two paths (PqCommitSealsSignedOnceTest). + * + * @param block the proposed block of this round + * @param commitHash its commit hash in this round + */ + void pqSealCommitOnce(final QbftBlock block, final Hash commitHash) { + if (commitHash.equals(pqSealedCommitHash) + && pqSealedFalcon.isPresent() + && (!pqSealedExtras.isEmpty() || !pqExtrasApplyAt(block.getHeader().getNumber()))) { + return; + } + final Optional falcon = falconSealFor(block, commitHash); + pqSealedExtras = + falcon.isPresent() ? extraSealsFor(block, commitHash) : java.util.List.of(); + pqSealedFalcon = falcon; + pqSealedCommitHash = commitHash; + } + /** * D-334 (2026-09-03, mainnet 2800, 125 s of halted chain): the hybrid loader is lazy, so a * registry the container could not read surfaced here, as an exception thrown INTO the QBFT * state machine, at every anchor parent, on every armed node. Three such nodes plus one node * restarting left five of nine. A broken hybrid configuration may cost this node its SLH-DSA * contribution; it must never cost the node its vote. The loader still refuses to START on the * same defect (QbftBesuControllerBuilder calls it at boot), so this catch is the second net. * * @param number the height being committed * @param message the seal message * @return the extras, or an empty list with an ERROR line when the hybrid support cannot sign */ static java.util.List hybridExtrasOrEmpty( final long number, final Bytes32 message) { try { return org.hyperledger.besu.consensus.common.bft.HybridSealSupport.instance() .producer() .sealsFor(number, message); } catch (final RuntimeException e) { LOG.error( "AERE HIBRID: extras NOT signed at height {} - {}. This node keeps voting but contributes no" + " SLH-DSA seal until the hybrid configuration is repaired (D-334).", number, e.getMessage()); return java.util.List.of(); } } /** The exact bytes a PQ seal over this block signs; shared by Falcon and the hybrid extras, * so the two halves of a hybrid certificate can never drift onto different messages. */ private Bytes32 pqSealMessageFor(final QbftBlock block, final Hash commitHash) { // AERE D-311 (2026-09-02): the form is decided in ONE place, shared with the verifier // (MessageValidator.SubsequentMessageValidator). A second copy of this rule drifted once and // stopped a chain; see PqAnchorProducer.commitSealMessage. return PqAnchorProducer.commitSealMessage( block.getHeader().getNumber(), () -> pqOnchainHashOf(block).getBytes(), commitHash); } - private Optional falconSealFor(final QbftBlock block, final Hash commitHash) { + Optional falconSealFor(final QbftBlock block, final Hash commitHash) { // FalconSealSupport.sign never throws (any fault is swallowed and logged), and returns empty // when this node holds no Falcon signing key, so the ECDSA commit path is never affected. // // AERE FIX-OPRIRE-CONSENS (b): the block NUMBER is now a mandatory argument. Seal ATTACHMENT is // gated on the activation height (and on registry coverage of the whole validator set) inside // FalconSealSupport.sign, so merely holding a Falcon key no longer makes a node emit a // Falcon-carrying Commit. There is deliberately no height-less sign() overload left: a call site // physically cannot bypass the gate. // // AERE ANCHOR V2: WHAT IS SIGNED changes one block BEFORE the anchor activates, because the // certificate carried by block H is made of seals over block H-1. From that height the message // is M = keccak256(RLP["AERE-PQ-COMMIT-1", chainId, number, ON-CHAIN block hash]) instead of the // ECDSA committed-seal hash. The committed-seal hash could not be used: it includes the ROUND, // which is not in the block-hash pre-image, so a validator holding only the parent header could // not rebuild it. Every node flips at the same height, since the height is a pure function of // the same configured H; a node configured with a different H emits seals nobody can use, and // the producer drops them on verification rather than carrying them into a header. // AERE HIBRID (2026-08-25): mesajul se calculeaza acum intr-UN singur loc, pqSealMessageFor, // impartit cu extrasele hibride; doua copii ale acestei logici ar fi divergat intr-o zi. final long blockNumber = block.getHeader().getNumber(); diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRoundFactory.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRoundFactory.java index f9c6cf7734..a7979513de 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRoundFactory.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/QbftRoundFactory.java @@ -1,36 +1,43 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.qbft.core.statemachine; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.qbft.core.network.QbftMessageTransmitter; import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreator; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreatorFactory; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockHeader; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockInterface; import org.hyperledger.besu.consensus.qbft.core.types.QbftFinalState; import org.hyperledger.besu.consensus.qbft.core.types.QbftMinedBlockObserver; import org.hyperledger.besu.consensus.qbft.core.types.QbftProtocolSchedule; import org.hyperledger.besu.consensus.qbft.core.validation.MessageValidatorFactory; import org.hyperledger.besu.util.Subscribers; /** The Qbft round factory. */ public class QbftRoundFactory { private final QbftFinalState finalState; private final QbftBlockCreatorFactory blockCreatorFactory; private final QbftBlockInterface blockInterface; private final QbftProtocolSchedule protocolSchedule; @@ -91,27 +98,27 @@ public class QbftRoundFactory { * @param parentHeader the parent header * @param roundState the round state * @return the qbft round */ public QbftRound createNewRoundWithState( final QbftBlockHeader parentHeader, final RoundState roundState) { final QbftBlockCreator blockCreator = blockCreatorFactory.create(roundState.getRoundIdentifier().getRoundNumber()); // TODO(tmm): Why is this created everytime?! final QbftMessageTransmitter messageTransmitter = new QbftMessageTransmitter(messageFactory, finalState.getValidatorMulticaster()); return new QbftRound( roundState, blockCreator, blockInterface, protocolSchedule, minedBlockObservers, finalState.getNodeKey(), - finalState.getLocalAddress(), + AerePqIdentity.local(finalState.getLocalAddress(), parentHeader.getNumber() + 1L), messageFactory, messageTransmitter, finalState.getRoundTimer(), parentHeader); } } diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java index 2c798c9e27..b7ba44b5a1 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/statemachine/RoundState.java @@ -149,40 +149,50 @@ public class RoundState { committed = (commitMessages.size() >= quorum) && proposalMessage.isPresent(); LOG.trace( "Round state updated prepared={} committed={} preparedQuorum={}/{} committedQuorum={}/{}", prepared, committed, prepareMessages.size(), quorum, commitMessages.size(), quorum); } /** * Gets proposed block. * * @return the proposed block */ public Optional 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 getProposalFalconSeal() { + return proposalMessage.flatMap(p -> p.getSignedPayload().getPayload().getFalconSeal()); + } + /** * Gets proposed block access list. * * @return the block access list */ public Optional getProposedBlockAccessList() { return proposalMessage.flatMap(Proposal::getBlockAccessList); } /** * Is prepared. * * @return the boolean */ public boolean isPrepared() { return prepared; } /** * Is committed. diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java index c397cab98f..af124c1ed0 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/types/QbftBlockCreator.java @@ -62,29 +62,45 @@ public interface QbftBlockCreator { /** * Create sealed block, additionally embedding a gossiped parallel Falcon-512 quorum certificate * (AERE hybrid PQC). The default implementation ignores the Falcon seals and delegates to the * ECDSA-only sealing, so existing implementations remain valid; the QBFT block creator adaptor * overrides this to aggregate the Falcon seals into a per-block quorum certificate. * * @param block the block * @param roundNumber the round number * @param commitSeals the decisive ECDSA commit seals * @param falconSeals the gossiped parallel Falcon-512 seals collected from commit messages * @return the block */ default QbftBlock createSealedBlock( final QbftBlock block, final int roundNumber, final Collection commitSeals, final Collection falconSeals) { 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. * * @return an empty collection of Falcon seals */ static Collection noFalconSeals() { return Collections.emptyList(); } } diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java index 918607ad2b..680c53f44a 100644 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/CommitValidator.java @@ -5,40 +5,41 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change: an OPTIONAL, height-gated post-quantum enforcement * hook (see PqCommitEnforcement). When no enforcement is supplied, behaviour is byte-for-byte the * upstream behaviour; the existing constructor keeps that contract for every existing caller. */ package org.hyperledger.besu.consensus.qbft.core.validation; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.payload.SignedData; import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Commit; import org.hyperledger.besu.consensus.qbft.core.payload.CommitPayload; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Util; import java.util.Collection; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** The Commit validator. */ public class CommitValidator { private static final String ERROR_PREFIX = "Invalid Commit Message"; private static final Logger LOG = LoggerFactory.getLogger(CommitValidator.class); @@ -190,41 +191,43 @@ public class CommitValidator { if (!payload.getRoundIdentifier().equals(targetRound)) { LOG.info( "{}: did not target expected round {} was {}", ERROR_PREFIX, targetRound, payload.getRoundIdentifier()); return false; } if (!payload.getDigest().equals(expectedDigest)) { LOG.info( "{}: did not contain expected digest {} was {}", ERROR_PREFIX, expectedDigest, payload.getDigest()); return false; } final Address commitSealCreator = - Util.signatureToAddress(payload.getCommitSeal(), expectedCommitDigest); + AerePqIdentity.author( + Util.signatureToAddress(payload.getCommitSeal(), expectedCommitDigest), + targetRound.getSequenceNumber()); if (!commitSealCreator.equals(signedPayload.getAuthor())) { LOG.info( "{}: Seal was not created by the message transmitter {} was {}", ERROR_PREFIX, commitSealCreator, signedPayload.getAuthor()); return false; } // AERE full-PQ: above the arming height a commit vote only counts with a valid post-quantum // seal bound to this very author. Below it (or with no enforcement supplied) nothing changes. if (pqEnforcement != null) { final Optional refusal = pqEnforcement.refusal( targetRound.getSequenceNumber(), signedPayload.getAuthor(), expectedPqSealMessage, payload.getFalconSeal(), payload.getExtraSeals()); diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java index d1aa1be8e1..074459cbe7 100755 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqCommitEnforcement.java @@ -1,40 +1,41 @@ /* * AERE full-PQ consensus (TOP 3 list, item 1), step 1: the enforcement core. * * WHAT IT DECIDES. From the arming height upward, a Commit message counts toward the 2f+1 * quorum ONLY if it carries the PQ seal (already transported in CommitPayload, live on the * fleet) and the seal (a) exists, (b) has its index bound to the VERY author of the message * through the height-indexed registry, (c) verifies over the commit digest. Without a valid * PQ seal the vote does not count -- this puts post-quantum into the agreement itself, at the * layer where the D-235 header rule could not live (headers between anchors carry no seals; * the commit message can carry them all). * * HOW IT IS WIRED (updated the same night). CommitValidator calls it through the production * constructor, which self-installs from fromSystemConfig(): absent property = null = * upstream behaviour, DISARMED by default; a broken value = loud refusal, never a silent * disarm. The core stays purely testable: the registry is injected (the PqSignerRegistry * interface, which refuses by construction to answer without a height -- the D2 inheritance); * the singleton enters only through the production factory, exactly like PqAnchorSealsRule. */ package org.hyperledger.besu.consensus.qbft.core.validation; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.FalconSeal; import org.hyperledger.besu.consensus.common.bft.HybridSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSignerRegistry; 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.consensus.common.bft.blockcreation.PqAnchorProducer; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.LongPredicate; import org.apache.tuweni.bytes.Bytes; /** Height-gated decision: does this Commit's PQ seal let it count toward quorum? */ @@ -96,40 +97,42 @@ public final class PqCommitEnforcement { // receives it too, so from the hybrid step of the schedule onward a vote without ALL the // required schemes does not count. Without the pair, this stays exactly the Falcon // enforcement we had until now. final HybridSealSupport hybrid = HybridSealSupport.instance(); if (hybrid.schedule().isPresent()) { return new PqCommitEnforcement( armedFrom, PqSignerRegistry.falconSealSupport(), hybrid.schedule().get(), hybrid.registry().orElseThrow()); } return new PqCommitEnforcement(armedFrom, PqSignerRegistry.falconSealSupport()); } private final long armedFromBlock; private final PqSignerRegistry registry; // AERE HIBRID: null in Falcon-only mode, which is every node today. private final PqSchemeSchedule schemeSchedule; private final HybridSignerRegistry hybridRegistry; private final LongPredicate extrasRequiredAt; + // AIP-22 stage 2: the origin of the anchor grids, read lazily so tests need no live config + private final java.util.function.LongSupplier anchorBlock; /** * @param armedFromBlock first block height (inclusive) at which enforcement applies; use * {@link #DISARMED} for the today-behaviour * @param registry the height-aware signer registry (injected, never a singleton) */ public PqCommitEnforcement(final long armedFromBlock, final PqSignerRegistry registry) { this(armedFromBlock, registry, null, null); } /** * Enforcement that also demands the HYBRID schemes a schedule requires at each height. * *

    AERE HIBRID (2026-08-25). Above the arming height a vote must carry, besides the Falcon * seal checked by the Falcon-only path, a valid seal for EVERY other scheme the schedule names * at that height. The point of a hybrid is that the two families fail independently, so a * partially satisfied certificate is worth exactly as much as the weakest family present, which * is why a missing scheme refuses rather than degrades. * *

    NO REGISTRY-ALIGNMENT ASSUMPTION. The hybrid registry and the legacy Falcon registry are @@ -162,45 +165,68 @@ public final class PqCommitEnforcement { * * @return the predicate "the block at this height is the parent of an anchor", from the live * anchor configuration */ public static LongPredicate anchorParentByConfig() { return h -> PqAnchorProducer.config().anchorAppliesAt(h + 1L); } /** * Full constructor. * * @param extrasRequiredAt at which heights the extra (non-Falcon) scheme seals are demanded on a * commit; production passes {@link #anchorParentByConfig()} */ public PqCommitEnforcement( final long armedFromBlock, final PqSignerRegistry registry, final PqSchemeSchedule schemeSchedule, final HybridSignerRegistry hybridRegistry, final LongPredicate extrasRequiredAt) { + this( + armedFromBlock, + registry, + schemeSchedule, + hybridRegistry, + extrasRequiredAt, + () -> PqAnchorProducer.config().anchorBlock()); + } + + /** + * Full constructor with an explicit anchor-grid origin (AIP-22 stage 2), for proofs that run + * a per-scheme interval schedule without the process-wide anchor configuration. + * + * @param anchorBlock supplies the chain's anchor activation height, origin of every grid + */ + public PqCommitEnforcement( + final long armedFromBlock, + final PqSignerRegistry registry, + final PqSchemeSchedule schemeSchedule, + final HybridSignerRegistry hybridRegistry, + final LongPredicate extrasRequiredAt, + final java.util.function.LongSupplier anchorBlock) { this.armedFromBlock = armedFromBlock; this.registry = registry; this.schemeSchedule = schemeSchedule; this.hybridRegistry = hybridRegistry; this.extrasRequiredAt = extrasRequiredAt; + this.anchorBlock = anchorBlock; if ((schemeSchedule == null) != (hybridRegistry == null)) { // Half a hybrid configuration is the shape that starts a node believing it enforces // something it does not. Refuse at construction, the same stance as every other AERE gate. throw new IllegalStateException( "AERE-PQC-COMMIT-CONF-03: the scheme schedule and the hybrid registry are a PAIR;" + " configure both or neither."); } } /** Whether enforcement is active at {@code height}. */ public boolean armedAt(final long height) { return height >= armedFromBlock; } /** * Decide whether a Commit may count toward quorum. * * @param height the block height the commit targets (the round's sequence number) * @param author the RECOVERED author of the signed Commit message (from its ECDSA signature) * @param commitDigest the commit digest the PQ seal must have signed @@ -226,88 +252,105 @@ public final class PqCommitEnforcement { * @return empty when the commit counts; otherwise the refusal, with names and numbers */ public Optional refusal( final long height, final Address author, final Hash commitDigest, final Optional seal, final List extraSeals) { final Optional falconVerdict = falconRefusal(height, author, commitDigest, seal); if (falconVerdict.isPresent() || !armedAt(height) || schemeSchedule == null || !extrasRequiredAt.test(height)) { return falconVerdict; } return hybridRefusal(height, author, commitDigest, seal.orElseThrow(), extraSeals); } /** * Every scheme the schedule names at this height, other than Falcon, must be present and valid. + * The height is an anchor PARENT (that is the only place extras are demanded), so the schemes + * are those the anchor at {@code height + 1} requires: with per-scheme intervals (AIP-22 stage + * 2) a commit at the parent of a Falcon-only anchor counts without an SLH-DSA seal. */ private Optional hybridRefusal( final long height, final Address author, final Hash commitDigest, final FalconSeal falconSeal, final List extraSeals) { - final Set required = schemeSchedule.schemesAt(height); + final Set required = + schemeSchedule.schemesRequiredForAnchor(height + 1L, anchorBlock.getAsLong()); for (final String schemeId : required) { if (SealSchemes.FALCON_512.id().equals(schemeId)) { continue; // already decided by the Falcon path above } final Optional scheme = SealSchemes.byId(schemeId); if (scheme.isEmpty()) { return Optional.of( "AERE HIBRID: the schedule requires scheme '" + schemeId + "' at height " + height + " and this binary does not implement it - refusing rather than ignoring it"); } final byte wire = scheme.get().wireId(); SchemeSeal found = null; for (final SchemeSeal candidate : extraSeals) { if (candidate.getSchemeWireId() == wire) { found = candidate; break; } } if (found == null) { return Optional.of( "AERE HIBRID: commit at height " + height + " carries no " + schemeId + " seal, which the schedule requires - the vote does not count"); } // One identity per message: the hybrid seal must speak for the same validator as the Falcon // seal, and that index must be THIS author in the hybrid registry. Neither is assumed. if (found.getValidatorIndex() != falconSeal.getValidatorIndex()) { return Optional.of( "AERE HIBRID: " + schemeId + " seal is index " + found.getValidatorIndex() + " but the Falcon seal on the same commit is index " + falconSeal.getValidatorIndex() + " - one commit, one signer"); } - final Optional bound = hybridRegistry.address(found.getValidatorIndex()); - if (bound.isEmpty() - || !Address.wrap(Bytes.wrap(bound.get())).equals(author)) { + // 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 + // producer and PqAnchorSealsRule. Paid 2026-09-23 on testnet 28001: this check alone still read the static v2 hybrid + // 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

    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( "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 publicKey = - hybridRegistry.publicKey(found.getValidatorIndex(), schemeId); + v3 + ? AerePqIdentity.v3KeyAt(height, found.getValidatorIndex(), schemeId) + : hybridRegistry.publicKey(found.getValidatorIndex(), schemeId); if (publicKey.isEmpty()) { return Optional.of( "AERE HIBRID: the hybrid registry holds no " + schemeId + " key for index " + found.getValidatorIndex()); } final boolean valid; try { valid = scheme .get() .verifyRaw( publicKey.get(), commitDigest.getBytes().toArray(), found.getSignature().toArray()); } catch (final RuntimeException e) { return Optional.of( "AERE HIBRID: " + schemeId + " verification threw at height " + height + ": " + e.getMessage()); } if (!valid) { diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java new file mode 100644 index 0000000000..ea85af1542 --- /dev/null +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuard.java @@ -0,0 +1,181 @@ +/* + * 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.core.validation; + +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.LongPredicate; + +/** + * AIP-22 stage 4: H_fin may be armed only where every post-quantum seal is VERIFIED from H_fin. + * + *

    From H_fin a message's author is the id of its seal's index ({@code AerePqFinality}), and the + * envelope's ECDSA signature is no longer read. For a PROPOSAL, PREPARE or ROUND-CHANGE the author is + * given only once the seal verifies; a COMMIT's author is its index's id only when that is the commit + * seal signer's id, and what judges the COMMIT's post-quantum seal is the commit layer alone. Each + * layer is armed on its own height. With the commit layer unset, or armed above H_fin, a Commit whose + * ECDSA commit seal was forged (the adversary stage 4 exists for) would count with any bytes as its + * post-quantum seal; and the other three layers are the second judge of what the author already + * checked. So this guard refuses to start such a node (AERE-PQC-FIN-02), before the network is up. + * Emergency disarming of a layer after H_fin therefore needs H_fin unset too, on every node: one + * uniform step back to ECDSA authorship, never a node that trusts unverified seals. + */ +public final class PqFinalityLayersGuard { + + /** + * One seal layer as the guard sees it. + * + * @param property the property that arms it + * @param armedAt whether it is armed at a height; null when the property is set nowhere + */ + public record Layer(String property, LongPredicate armedAt) {} + + private PqFinalityLayersGuard() {} + + /** + * The refusal, if any. + * + * @param hFin H_fin, or {@link AerePqIdentity#NEVER} + * @param layers the four seal layers + * @return empty when H_fin is unset or every layer is armed at or below it; otherwise the refusal + */ + public static Optional refusal(final long hFin, final List layers) { + if (hFin == AerePqIdentity.NEVER) { + return Optional.empty(); + } + final List missing = new ArrayList<>(); + for (final Layer l : layers) { + if (l.armedAt() == null || !l.armedAt().test(hFin)) { + missing.add(l.property() + (l.armedAt() == null ? " unset" : " armed above " + hFin)); + } + } + if (missing.isEmpty()) { + return Optional.empty(); + } + return Optional.of( + "AERE-PQC-FIN-02: aere.pq.finalityBlock=" + + hFin + + " makes a message's author the id of its post-quantum seal's index, so every seal" + + " must be verified from H_fin; not armed at or below it: " + + String.join(", ", missing) + + ". Arm the four layers at or below H_fin on every node, or unset H_fin on every node."); + } + + /** + * The four layers as this node's configuration arms them. + * + * @return proposal, prepare, commit, round-change + */ + public static List fromSystemConfig() { + final PqProposalEnforcement p = PqProposalEnforcement.fromSystemConfig(); + final PqPrepareEnforcement pr = PqPrepareEnforcement.fromSystemConfig(); + final PqCommitEnforcement c = PqCommitEnforcement.fromSystemConfig(); + final PqRoundChangeEnforcement rc = PqRoundChangeEnforcement.fromSystemConfig(); + return List.of( + new Layer(PqProposalEnforcement.PROPERTY_FORK_BLOCK, p == null ? null : p::armedAt), + new Layer(PqPrepareEnforcement.PROPERTY_FORK_BLOCK, pr == null ? null : pr::armedAt), + new Layer(PqCommitEnforcement.PROPERTY_FORK_BLOCK, c == null ? null : c::armedAt), + new Layer( + PqRoundChangeEnforcement.PROPERTY_FORK_BLOCK, rc == null ? null : rc::armedAt)); + } + + /** + * Refuses to start when H_fin is armed over an unverified layer. + * + * @param hFin H_fin as armed on this node + * @throws IllegalStateException AERE-PQC-FIN-02 + */ + public static void verifyOrAbort(final long hFin) { + final Optional r = refusal(hFin, fromSystemConfig()); + if (r.isPresent()) { + throw new IllegalStateException(r.get()); + } + } + + /** + * AERE-PQC-FIN-03: from H_fin an unsealed message has no author, so a node that seals (holds its + * Falcon key; from H_k every registered validator must, stage 3b-ii) must seal all four messages at + * or below H_fin, or it goes mute at H_fin while looking healthy: a loss of liveness, not safety. + * + * @param hFin H_fin, or {@link AerePqIdentity#NEVER} + * @param signs whether this node seals (a reader does not) + * @param attachCommit aere.falcon.attachBlock ({@link Long#MAX_VALUE} when unset) + * @param attachProposal aere.pq.proposalPq.attachBlock + * @param attachPrepare aere.pq.preparePq.attachBlock + * @param attachRoundChange aere.pq.roundChangePq.attachBlock + * @return the refusal, or empty + */ + public static Optional emissionRefusal( + final long hFin, + final boolean signs, + final long attachCommit, + final long attachProposal, + final long attachPrepare, + final long attachRoundChange) { + if (hFin == AerePqIdentity.NEVER || !signs) { + return Optional.empty(); + } + final List missing = new ArrayList<>(); + gate(missing, "aere.falcon.attachBlock", attachCommit, hFin); + gate(missing, "aere.pq.proposalPq.attachBlock", attachProposal, hFin); + gate(missing, "aere.pq.preparePq.attachBlock", attachPrepare, hFin); + gate(missing, "aere.pq.roundChangePq.attachBlock", attachRoundChange, hFin); + if (missing.isEmpty()) { + return Optional.empty(); + } + return Optional.of( + "AERE-PQC-FIN-03: aere.pq.finalityBlock=" + + hFin + + " makes an unsealed message authorless, and this node would not seal from H_fin: " + + String.join(", ", missing) + + ". It would go mute at H_fin. Arm them at or below H_fin, or unset H_fin, on every validator."); + } + + private static void gate(final List missing, final String property, final long h, final long hFin) { + if (h < 0 || h == Long.MAX_VALUE) { + missing.add(property + " unset"); + } else if (h > hFin) { + missing.add(property + " armed above H_fin (" + h + ")"); + } + } + + /** + * Refuses to start when a sealing node would not seal every message from H_fin. + * + * @param hFin H_fin as armed on this node + * @param signs whether this node seals + * @param attachCommit commit attachment height + * @param attachProposal proposal attachment height + * @param attachPrepare prepare attachment height + * @param attachRoundChange round-change attachment height + * @throws IllegalStateException AERE-PQC-FIN-03 + */ + public static void verifyEmissionOrAbort( + final long hFin, + final boolean signs, + final long attachCommit, + final long attachProposal, + final long attachPrepare, + final long attachRoundChange) { + final Optional r = + emissionRefusal(hFin, signs, attachCommit, attachProposal, attachPrepare, attachRoundChange); + if (r.isPresent()) { + throw new IllegalStateException(r.get()); + } + } +} diff --git a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidator.java b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidator.java old mode 100644 new mode 100755 index 8fd9bed3e6..63a94d5ae5 --- a/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidator.java +++ b/consensus/qbft-core/src/main/java/org/hyperledger/besu/consensus/qbft/core/validation/RoundChangePayloadValidator.java @@ -38,40 +38,43 @@ import org.slf4j.LoggerFactory; * performed elsewhere (eg. the BlockHeightManager) * *

    AERE (2026-08-31): an OPTIONAL post-quantum enforcement hook, gated on height (see {@link * PqRoundChangeEnforcement}). When no enforcement is supplied, behaviour is byte for byte the * upstream one - and that is the configuration of every node today. Same pattern as the PREPARE, * commit and PROPOSAL validators, deliberately: a second rendering of the same idea, written * differently, diverges eventually. * *

    The 2-argument constructor - the one production code calls, from both {@code * MessageValidatorFactory} and {@code ProposalValidator} (the justification path) - wires the * enforcement itself from the system configuration. That self-wiring is the security property: an * enforcement that had to be passed in explicitly could be dropped by any refactor without a * single test failing. It also means round-changes are judged in BOTH places they arrive: * standalone, and inside a proposal's round-change certificate. */ public class RoundChangePayloadValidator { private static final String ERROR_PREFIX = "Invalid RoundChange Payload"; private static final Logger LOG = LoggerFactory.getLogger(RoundChangePayloadValidator.class); + /** maximum allowed round (AERE D-434: ported from upstream 118f8b1920, GHSA-23rh-rrqg-wq82) */ + public static final int MAX_ALLOWED_ROUND = 1000; + private final Collection

    validators; private final long chainHeight; private final PqRoundChangeEnforcement pqEnforcement; /** * Instantiates a new Round change payload validator, with the post-quantum enforcement wired * from the system configuration. This is the constructor production code calls. * * @param validators the validators * @param chainHeight the chain height */ public RoundChangePayloadValidator(final Collection
    validators, final long chainHeight) { this(validators, chainHeight, PqRoundChangeEnforcement.fromSystemConfig()); } /** * Instantiates a new Round change payload validator with an explicit post-quantum enforcement. * Test seam; production goes through the 2-argument constructor above. * * @param validators the validators @@ -92,42 +95,42 @@ public class RoundChangePayloadValidator { * Validate. * * @param signedPayload the signed payload * @return the boolean */ public boolean validate(final SignedData signedPayload) { if (!validators.contains(signedPayload.getAuthor())) { LOG.info("{}: did not originate from a recognized validator.", ERROR_PREFIX); return false; } final RoundChangePayload payload = signedPayload.getPayload(); if (payload.getRoundIdentifier().getSequenceNumber() != chainHeight) { LOG.info("{}: did not target expected height", ERROR_PREFIX); return false; } final int targetRound = payload.getRoundIdentifier().getRoundNumber(); - if (targetRound <= 0) { - LOG.info("{}: must contain a positive target round number", ERROR_PREFIX); + if (targetRound <= 0 || targetRound > MAX_ALLOWED_ROUND) { + LOG.info("{}: target round number out of range [1, {}]", ERROR_PREFIX, MAX_ALLOWED_ROUND); return false; } if (payload.getPreparedRoundMetadata().isPresent()) { final PreparedRoundMetadata metadata = payload.getPreparedRoundMetadata().get(); if (metadata.getPreparedRound() >= targetRound) { LOG.info("{}: prepared metadata is from a round ahead of target round", ERROR_PREFIX); return false; } if (metadata.getPreparedRound() < 0) { LOG.info("{}: prepared metadata is from a negative round number", ERROR_PREFIX); return false; } } // AERE full-PQ: from the armed height, a round-change is not accepted without a valid // post-quantum seal from its OWN author. Below the height, and on every node without the // property, this is a null check and nothing more - the upstream path, call for call. if (pqEnforcement != null) { diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/AereD434ListBoundsTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/AereD434ListBoundsTest.java new file mode 100644 index 0000000000..889e0e7c20 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/messagewrappers/AereD434ListBoundsTest.java @@ -0,0 +1,73 @@ +/* + * Copyright contributors to Besu. + * + * 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 + * + * Aere Network notice (Apache-2.0 section 4(b)): this file was added by Aere Network, 2026-09-28 (D-434), adapted from the tests of the + * ported upstream fix hyperledger/besu dd4101c9ec ("bound prepares/round-change lists at decode"). + */ +package org.hyperledger.besu.consensus.qbft.core.messagewrappers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.messagewrappers.BftMessage; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.core.payload.PreparePayload; +import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; +import org.hyperledger.besu.cryptoservices.NodeKey; +import org.hyperledger.besu.cryptoservices.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPException; + +import java.util.Collections; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * AERE D-434: a RoundChange whose prepares list holds more than BftMessage.MAX_LIST_ENTRIES (512) entries is refused at DECODE, before + * any signature or author check; before the port any peer could make a node allocate and verify an arbitrarily long list. With ten + * validators an honest list never exceeds ten, so the bound never binds an honest network. + */ +class AereD434ListBoundsTest { + + private static final QbftBlockCodec CODEC = Mockito.mock(QbftBlockCodec.class); + + private static Bytes roundChangeWith(final int prepares) { + final NodeKey key = NodeKeyUtils.generate(); + final RoundChangePayload rc = new RoundChangePayload(new ConsensusRoundIdentifier(1, 0), Optional.empty()); + final SignedData signedRc = SignedData.create(rc, key.sign(Bytes32.wrap(rc.hashForSignature().getBytes()))); + final PreparePayload p = new PreparePayload(new ConsensusRoundIdentifier(1, 0), Hash.ZERO); + final SignedData one = SignedData.create(p, key.sign(Bytes32.wrap(p.hashForSignature().getBytes()))); + return new RoundChange(signedRc, Optional.empty(), Optional.empty(), CODEC, Collections.nCopies(prepares, one)).encode(); + } + + @Test + void aPreparesListAboveTheBoundIsRefusedAtDecode() { + final Bytes oversized = roundChangeWith(BftMessage.MAX_LIST_ENTRIES + 1); + assertThatThrownBy(() -> RoundChange.decode(oversized, CODEC)) + .isInstanceOf(RLPException.class) + .hasMessageContaining("exceeds the maximum permitted size"); + } + + @Test + void aPreparesListAtTheBoundStillDecodes() { + // positive control: the fixture decodes, and the bound is inclusive + assertThat(RoundChange.decode(roundChangeWith(BftMessage.MAX_LIST_ENTRIES), CODEC).getPrepares()).hasSize(BftMessage.MAX_LIST_ENTRIES); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/statemachine/PqCommitSealsSignedOnceTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/statemachine/PqCommitSealsSignedOnceTest.java new file mode 100644 index 0000000000..daa5ff1d44 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/statemachine/PqCommitSealsSignedOnceTest.java @@ -0,0 +1,210 @@ +/* + * Copyright contributors to 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.core.statemachine; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.FalconSeal; +import org.hyperledger.besu.consensus.common.bft.PqSealCache; +import org.hyperledger.besu.consensus.common.bft.RoundTimer; +import org.hyperledger.besu.consensus.common.bft.SchemeSeal; +import org.hyperledger.besu.consensus.qbft.core.QbftBlockTestFixture; +import org.hyperledger.besu.consensus.qbft.core.network.QbftMessageTransmitter; +import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreator; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockHeader; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockInterface; +import org.hyperledger.besu.consensus.qbft.core.types.QbftMinedBlockObserver; +import org.hyperledger.besu.consensus.qbft.core.types.QbftProtocolSchedule; +import org.hyperledger.besu.consensus.qbft.core.validation.MessageValidator; +import org.hyperledger.besu.consensus.qbft.core.validation.QbftBlockHeaderTestFixture; +import org.hyperledger.besu.cryptoservices.NodeKey; +import org.hyperledger.besu.cryptoservices.NodeKeyUtils; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.util.Subscribers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +/** + * D-337 (2026-09-28): this node's Commit seals are signed ONCE per round, not once per path. + * + *

    The measured cost on chain 2800: at the parent of every anchor each Besu validator signed + * SLH-DSA twice back to back on the consensus thread - on accepting the proposal (before its + * Prepare) and again on becoming prepared (between "prepared" and its Commit) - and the + * anchor-parent slot ran at 1,036 ms against 599 for the rest. + * + *

    The round is driven through the real state machine: a peer's proposal, the local Prepare, a + * peer's Prepare that makes the round prepared, and the Commit that goes on the wire. The signers + * are stubbed on a spy so the SIGNINGS can be counted; everything between them is the production + * code. The pair of the claim is the retry: an attempt that produced no Falcon seal must be signed + * again on the second path, exactly as before the change, so the reuse cannot swallow a failure. + */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class PqCommitSealsSignedOnceTest { + + private static final long HEIGHT = 7L; + + private final NodeKey nodeKey = NodeKeyUtils.generate(); + private final Address localAddress = Address.extract(nodeKey.getPublicKey()); + private final NodeKey peerKey = NodeKeyUtils.generate(); + private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(HEIGHT, 0); + private final Subscribers subscribers = Subscribers.create(); + private final FalconSeal falcon = new FalconSeal(4, Bytes.fromHexString("0x29aabbcc")); + private final SchemeSeal slh = new SchemeSeal((byte) 0x02, 4, Bytes.fromHexString("0x0102030405")); + + private MessageFactory localFactory; + private MessageFactory peerFactory; + private QbftBlock proposedBlock; + + @Mock private QbftBlockCodec blockEncoder; + @Mock private QbftProtocolSchedule protocolSchedule; + @Mock private QbftMessageTransmitter transmitter; + @Mock private MessageValidator messageValidator; + @Mock private RoundTimer roundTimer; + @Mock private QbftBlockCreator blockCreator; + @Mock private QbftBlockInterface blockInterface; + + private final QbftBlockHeader parentHeader = + new QbftBlockHeaderTestFixture().number(HEIGHT - 1).buildHeader(); + + @BeforeEach + public void setup() { + PqSealCache.instance().clear(); + localFactory = new MessageFactory(nodeKey, blockEncoder); + peerFactory = new MessageFactory(peerKey, blockEncoder); + when(messageValidator.validateProposal(any())).thenReturn(true); + when(messageValidator.validatePrepare(any())).thenReturn(true); + when(messageValidator.validateCommit(any())).thenReturn(true); + proposedBlock = + new QbftBlockTestFixture() + .blockHeader(new QbftBlockHeaderTestFixture().number(HEIGHT).buildHeader()) + .build(); + when(blockInterface.replaceRoundForCommitBlock(any(), anyInt())).thenReturn(proposedBlock); + } + + @AfterEach + public void clean() { + PqSealCache.instance().clear(); + } + + /** A round of quorum 2: the local Prepare plus ONE peer Prepare make it prepared. */ + private QbftRound spiedRound() { + final RoundState roundState = new RoundState(roundIdentifier, 2, messageValidator); + return spy( + new QbftRound( + roundState, + blockCreator, + blockInterface, + protocolSchedule, + subscribers, + nodeKey, + localAddress, + localFactory, + transmitter, + roundTimer, + parentHeader)); + } + + /** Proposal from the peer, then the peer's Prepare: the second path fires on the second step. */ + private void driveToPrepared(final QbftRound round) { + round.handleProposalMessage( + peerFactory.createProposal( + roundIdentifier, proposedBlock, Collections.emptyList(), Collections.emptyList())); + assertThat(round.getRoundState().isPrepared()).isFalse(); + round.handlePrepareMessage(peerFactory.createPrepare(roundIdentifier, proposedBlock.getHash())); + assertThat(round.getRoundState().isPrepared()).isTrue(); + } + + @Test + public void anAnchorParentCommitIsSignedOnceAndTheWireCarriesThoseSeals() { + final QbftRound round = spiedRound(); + doReturn(Optional.of(falcon)).when(round).falconSealFor(any(), any()); + doReturn(List.of(slh)).when(round).extraSealsFor(any(), any()); + + driveToPrepared(round); + + verify(round, times(1)).falconSealFor(any(), any()); + verify(round, times(1)).extraSealsFor(any(), any()); + verify(transmitter, times(1)) + .multicastCommit( + eq(roundIdentifier), + eq(proposedBlock.getHash()), + any(), + eq(Optional.of(falcon)), + eq(List.of(slh))); + // the local Commit, added on accepting the proposal, carries the very same extras + assertThat(round.getRoundState().getExtraSeals()).containsExactly(slh); + } + + @Test + public void aFalconOnlyCommitOffTheExtrasGridIsAlsoSignedOnce() { + // off the anchor-parent predicate the extras are legitimately empty; a Falcon-only result is + // complete there and must be reused too, not re-signed + assertThat(QbftRound.pqExtrasApplyAt(HEIGHT)).isFalse(); + final QbftRound round = spiedRound(); + doReturn(Optional.of(falcon)).when(round).falconSealFor(any(), any()); + doReturn(List.of()).when(round).extraSealsFor(any(), any()); + + driveToPrepared(round); + + verify(round, times(1)).falconSealFor(any(), any()); + verify(transmitter, times(1)) + .multicastCommit( + eq(roundIdentifier), eq(proposedBlock.getHash()), any(), eq(Optional.of(falcon))); + } + + @Test + public void aFirstAttemptWithoutAFalconSealIsSignedAgainOnTheSecondPath() { + // the pair: reuse must never swallow a failed attempt; before D-337 the second path signed + // anew and could succeed, and it still must + final QbftRound round = spiedRound(); + doReturn(Optional.empty()).doReturn(Optional.of(falcon)).when(round).falconSealFor(any(), any()); + doReturn(List.of(slh)).when(round).extraSealsFor(any(), any()); + + driveToPrepared(round); + + verify(round, times(2)).falconSealFor(any(), any()); + verify(transmitter, times(1)) + .multicastCommit( + eq(roundIdentifier), + eq(proposedBlock.getHash()), + any(), + eq(Optional.of(falcon)), + eq(List.of(slh))); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereD434RoundCapTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereD434RoundCapTest.java new file mode 100644 index 0000000000..e719908194 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereD434RoundCapTest.java @@ -0,0 +1,61 @@ +/* + * Copyright contributors to Besu. + * + * 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 + * + * Aere Network notice (Apache-2.0 section 4(b)): this file was added by Aere Network, 2026-09-28 (D-434), as the regression test of + * the ported upstream fix hyperledger/besu 118f8b1920 (GHSA-23rh-rrqg-wq82, "Cap BFT round change number"). + */ +package org.hyperledger.besu.consensus.qbft.core.validation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; +import org.hyperledger.besu.cryptoservices.NodeKey; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.Test; + +/** + * AERE D-434: a RoundChange for a round above RoundChangePayloadValidator.MAX_ALLOWED_ROUND (1000) is refused, as upstream does since + * 2026-08-20. Before, any positive round was accepted, so one validator could push the round-change bookkeeping of every node to an + * arbitrary round. The worst round the live chains have reached is 6 (2026-09-04), so the cap never binds an honest network. + */ +class AereD434RoundCapTest { + + private static final long HEIGHT = 5L; + + private static boolean judge(final int round) { + final QbftNodeList validators = QbftNodeList.createNodes(4, null); + final RoundChangePayloadValidator v = new RoundChangePayloadValidator(validators.getNodeAddresses(), HEIGHT); + final RoundChangePayload payload = new RoundChangePayload(new ConsensusRoundIdentifier(HEIGHT, round), Optional.empty()); + final NodeKey key = validators.getNode(0).getNodeKey(); + return v.validate(SignedData.create(payload, key.sign(Bytes32.wrap(payload.hashForSignature().getBytes())))); + } + + @Test + void aRoundAboveTheCapIsRefused() { + assertThat(judge(RoundChangePayloadValidator.MAX_ALLOWED_ROUND + 1)).withFailMessage("round 1001 accepted").isFalse(); + assertThat(judge(Integer.MAX_VALUE)).withFailMessage("round 2^31-1 accepted").isFalse(); + } + + @Test + void theCapItselfAndOrdinaryRoundsAreStillAccepted() { + // positive control: the fixture is valid, and the cap is inclusive + assertThat(judge(1)).isTrue(); + assertThat(judge(RoundChangePayloadValidator.MAX_ALLOWED_ROUND)).isTrue(); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityMessageTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityMessageTest.java new file mode 100644 index 0000000000..e8df550557 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityMessageTest.java @@ -0,0 +1,204 @@ +/* + * 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.core.validation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.AerePqFinality; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +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.common.bft.payload.Payload; +import org.hyperledger.besu.consensus.common.bft.payload.SignedData; +import org.hyperledger.besu.consensus.qbft.core.QbftBlockTestFixture; +import org.hyperledger.besu.consensus.qbft.core.payload.CommitPayload; +import org.hyperledger.besu.consensus.qbft.core.payload.PreparePayload; +import org.hyperledger.besu.consensus.qbft.core.payload.PreparedRoundMetadata; +import org.hyperledger.besu.consensus.qbft.core.payload.ProposalPayload; +import org.hyperledger.besu.consensus.qbft.core.payload.RoundChangePayload; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlock; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; +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.util.ArrayList; +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; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * AIP-22 stage 4 on the REAL QBFT payloads, with real Falcon-512 keys generated for the run: from H_fin + * the author of a PREPARE, a PROPOSAL and a ROUND-CHANGE is the id of its seal's index only when the + * seal verifies over the message its own layer builds, and a COMMIT's only when that index names the + * commit seal signer's id. Every envelope is signed by a FOREIGN node key, so a validator's id can only + * come from the seal. Each positive has its negative on the same fixture: another index's seal, a seal + * over another round, a ROUND-CHANGE with prepared metadata sealed in the unprepared form, a proposal + * sealed over another block, a commit seal of another validator. The runner's negative control plants + * "every seal verifies" at run time and these probes turn red. + */ +@ExtendWith(MockitoExtension.class) +public class AereFinalityMessageTest { + + private static final long CHAIN = 28001; + private static final long BIND = 10; // H_k must be the bindHeight of a v3 epoch at or above H_id + private static final long H_ID = 10; + private static final long H_FIN = 20; + private static final int N = 4; + private static final SealScheme FALCON = SealSchemes.FALCON_512; + + private final Hash digest = Hash.hash(Bytes.of(7)); + private final NodeKey foreign = NodeKeyUtils.generate(); + private final List ecdsa = new ArrayList<>(); + private final List falcon = new ArrayList<>(); + private final List

    ids = new ArrayList<>(); + private @Mock QbftBlockCodec blockCodec; + + @BeforeEach + public void registry() { + final List rows = new ArrayList<>(); + for (int i = 0; i < N; i++) { + final NodeKey nk = NodeKeyUtils.generate(); + ecdsa.add(nk); + 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 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 Bytes32 rh = PqRegistryV3.rowHash(CHAIN, BIND, N, bare); + final SECPSignature claim = nk.sign(PqRegistryV3.claimDigest(CHAIN, rh)); + final Address addr = org.hyperledger.besu.ethereum.core.Util.publicKeyToAddress(nk.getPublicKey()); + rows.add( + new PqRegistryV3.Row( + i, id, keys, null, Map.of(), addr.getBytes().toArrayUnsafe(), + claim.encodedBytes().toArrayUnsafe())); + ids.add(Address.wrap(Bytes.wrap(id))); + } + final List hashes = new ArrayList<>(); + for (final PqRegistryV3.Row r : rows) { + hashes.add(PqRegistryV3.rowHash(CHAIN, BIND, N, r)); + } + final Bytes32 root = PqRegistryV3.merkleRoot(hashes); + final PqRegistryV3.Registry reg = + new PqRegistryV3.Registry( + CHAIN, BIND, N, rows, hashes, root, PqRegistryV3.registryHash(CHAIN, BIND, N, root)); + final TreeMap epochs = new TreeMap<>(); + epochs.put(BIND, reg); + AerePqFinality.uninstall(); + AerePqIdentity.uninstall(); + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs, H_ID)); + // the production verifier reads the same v3 key from H_k (FalconSealSupport.verifyAtOwnHead) + AerePqFinality.install( + H_FIN, + CHAIN, + (h, i, m, s) -> + AerePqIdentity.v3KeyAt(h, i, "falcon-512") + .map(k -> FALCON.verifyRaw(k, m.toArrayUnsafe(), s.toArrayUnsafe())) + .orElse(false)); + } + + @AfterEach + public void disarm() { + AerePqFinality.uninstall(); + AerePqIdentity.uninstall(); + } + + private Optional seal(final int index, final int signer, final Bytes32 message) { + return Optional.of( + new FalconSeal(index, Bytes.wrap(FALCON.sign(falcon.get(signer), message.toArrayUnsafe()).orElseThrow()))); + } + + private

    Address authorOf(final P payload) { + return SignedData.create(payload, foreign.sign(Bytes32.wrap(payload.hashForSignature().getBytes()))) + .getAuthor(); + } + + private static ConsensusRoundIdentifier round(final int r) { + return new ConsensusRoundIdentifier(H_FIN, r); + } + + @Test + public void aPrepareIsAuthoredOnlyByAVerifiedSealOfItsIndex() { + final Bytes32 m = PqAnchor.prepareMessage(CHAIN, H_FIN, 0, digest.getBytes()); + for (int i = 0; i < N; i++) { + assertThat(authorOf(new PreparePayload(round(0), digest, seal(i, i, m)))).as("node %s", i).isEqualTo(ids.get(i)); + } + // index 0 with node 1's seal; node 0's seal over round 1; no seal + assertThat(authorOf(new PreparePayload(round(0), digest, seal(0, 1, m)))).isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat( + authorOf( + new PreparePayload( + round(0), digest, seal(0, 0, PqAnchor.prepareMessage(CHAIN, H_FIN, 1, digest.getBytes()))))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + assertThat(authorOf(new PreparePayload(round(0), digest, Optional.empty()))).isEqualTo(AerePqFinality.NO_AUTHOR); + } + + @Test + public void aRoundChangeIsAuthoredOnlyByASealInTheFormItsMetadataNames() { + final Bytes32 bare = PqAnchor.roundChangeMessage(CHAIN, H_FIN, 2); + assertThat(authorOf(new RoundChangePayload(round(2), Optional.empty(), seal(1, 1, bare)))).isEqualTo(ids.get(1)); + final Optional meta = Optional.of(new PreparedRoundMetadata(digest, 1)); + final Bytes32 prepared = PqAnchor.roundChangeMessage(CHAIN, H_FIN, 2, 1, digest.getBytes()); + assertThat(authorOf(new RoundChangePayload(round(2), meta, seal(1, 1, prepared)))).isEqualTo(ids.get(1)); + // the prepared claim sealed in the unprepared form does not author: the seal must cover the claim + assertThat(authorOf(new RoundChangePayload(round(2), meta, seal(1, 1, bare)))).isEqualTo(AerePqFinality.NO_AUTHOR); + } + + @Test + public void aProposalIsAuthoredOnlyByASealOverItsOwnBlock() { + final QbftBlock block = + new QbftBlockTestFixture() + .blockHeader(new QbftBlockHeaderTestFixture().number(H_FIN).buildHeader()) + .build(); + final Bytes32 m = PqAnchor.proposalMessage(CHAIN, H_FIN, 0, block.getHash().getBytes()); + assertThat(authorOf(new ProposalPayload(round(0), block, blockCodec, Optional.empty(), seal(2, 2, m)))) + .isEqualTo(ids.get(2)); + final Bytes32 other = PqAnchor.proposalMessage(CHAIN, H_FIN, 0, digest.getBytes()); + assertThat(authorOf(new ProposalPayload(round(0), block, blockCodec, Optional.empty(), seal(2, 2, other)))) + .isEqualTo(AerePqFinality.NO_AUTHOR); + } + + @Test + public void aCommitIsAuthoredByItsIndexOnlyWhenThatIsTheCommitSealSigner() { + final Optional any = Optional.of(new FalconSeal(3, Bytes.of(1))); + final SECPSignature own = ecdsa.get(3).sign(Bytes32.wrap(digest.getBytes())); + final SECPSignature other = ecdsa.get(0).sign(Bytes32.wrap(digest.getBytes())); + assertThat(authorOf(new CommitPayload(round(0), digest, own, any))).isEqualTo(ids.get(3)); + assertThat(authorOf(new CommitPayload(round(0), digest, other, any))).isEqualTo(AerePqFinality.NO_AUTHOR); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityVectorsWriter.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityVectorsWriter.java new file mode 100644 index 0000000000..b1a6776bb0 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereFinalityVectorsWriter.java @@ -0,0 +1,206 @@ +/* + * 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.core.validation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.AerePqFinality; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; +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.core.messagewrappers.Commit; +import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Prepare; +import org.hyperledger.besu.consensus.qbft.core.messagewrappers.RoundChange; +import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; +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 org.hyperledger.besu.ethereum.core.Util; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +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.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.mockito.Mockito; + +/** + * AIP-22 stage 4: the WRITER of the shared authorship vectors. Real QBFT messages, encoded on the wire + * exactly as this client sends them, each ENVELOPE signed by a FOREIGN node key, each carrying a real + * Falcon-512 seal (keys generated for the run, nothing here touches a real key): the second client must + * decode every one to the SAME author this client gives it. Every expected author is asserted here, at + * writing time, through this client's own decoding path (SignedData), so a vector cannot say what this + * client does not do. Runs only with AERE_T4_VECTORS_OUT set to the output directory. + */ +@EnabledIfEnvironmentVariable(named = "AERE_T4_VECTORS_OUT", matches = ".+") +public class AereFinalityVectorsWriter { + + 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 int N = 4; + private static final SealScheme FALCON = SealSchemes.FALCON_512; + + @AfterEach + public void disarm() { + AerePqFinality.uninstall(); + AerePqIdentity.uninstall(); + } + + private static String hex(final Bytes b) { + return b.toUnprefixedHexString(); + } + + @Test + public void writeTheSharedAuthorshipVectors() throws IOException { + final List ecdsa = new ArrayList<>(); + final List falcon = new ArrayList<>(); + final List std = new ArrayList<>(); + final List

    ids = new ArrayList<>(); + final List rows = new ArrayList<>(); + for (int i = 0; i < N; i++) { + final NodeKey nk = NodeKeyUtils.generate(); + ecdsa.add(nk); + final SealScheme.GeneratedPair p = FALCON.generate(SecureRandomProvider.createSecureRandom()); + falcon.add(p.privateKey()); + final byte[] s = new byte[897]; + s[0] = 0x09; + System.arraycopy(p.publicRegistryForm(), 0, s, 1, 896); + std.add(s); + final byte[] id = PqRegistryV3.idOf(s); + final Bytes32 popMsg = PqRegistryV3.popMessage(CHAIN, BIND, N, i, id, FALCON.wireId(), s); + final byte[] pop = FALCON.sign(p.privateKey(), popMsg.toArrayUnsafe()).orElseThrow(); + final TreeMap keys = new TreeMap<>(); + keys.put(FALCON.wireId() & 0xff, new PqRegistryV3.Key(FALCON, s, 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 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 epochs = new TreeMap<>(); + epochs.put( + BIND, + new PqRegistryV3.Registry( + CHAIN, BIND, N, rows, hashes, root, PqRegistryV3.registryHash(CHAIN, BIND, N, root))); + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs, H_ID)); + AerePqFinality.install( + H_FIN, + CHAIN, + (h, i, m, s) -> + AerePqIdentity.v3KeyAt(h, i, "falcon-512") + .map(k -> FALCON.verifyRaw(k, m.toArrayUnsafe(), s.toArrayUnsafe())) + .orElse(false)); + + final NodeKey foreign = NodeKeyUtils.generate(); + final Address foreignAddr = Util.publicKeyToAddress(foreign.getPublicKey()); + final MessageFactory mf = new MessageFactory(foreign, Mockito.mock(QbftBlockCodec.class)); + final Hash digest = Hash.hash(Bytes.of(7)); + final List out = new ArrayList<>(); // name, type, height, wire, expected author + + // 1. a PREPARE sealed by its own index: the index's id + final Bytes32 pm = PqAnchor.prepareMessage(CHAIN, H_FIN, 0, digest.getBytes()); + final Prepare p1 = mf.createPrepare(new ConsensusRoundIdentifier(H_FIN, 0), digest, seal(falcon, 1, 1, pm)); + assertThat(p1.getAuthor()).isEqualTo(ids.get(1)); + out.add(new String[] {"prepare-own-seal", "prepare", Long.toString(H_FIN), hex(p1.encode()), hex(ids.get(1).getBytes())}); + // 2. a PREPARE naming index 0 with index 1's seal: no author + final Prepare p2 = mf.createPrepare(new ConsensusRoundIdentifier(H_FIN, 0), digest, seal(falcon, 0, 1, pm)); + assertThat(p2.getAuthor()).isEqualTo(AerePqFinality.NO_AUTHOR); + out.add(new String[] {"prepare-another-index-seal", "prepare", Long.toString(H_FIN), hex(p2.encode()), hex(AerePqFinality.NO_AUTHOR.getBytes())}); + // 3. a PREPARE one block below H_fin: the envelope's (foreign) signer + final Bytes32 pmb = PqAnchor.prepareMessage(CHAIN, H_FIN - 1, 0, digest.getBytes()); + final Prepare p3 = mf.createPrepare(new ConsensusRoundIdentifier(H_FIN - 1, 0), digest, seal(falcon, 1, 1, pmb)); + assertThat(p3.getAuthor()).isEqualTo(foreignAddr); + out.add(new String[] {"prepare-below-hfin", "prepare", Long.toString(H_FIN - 1), hex(p3.encode()), hex(foreignAddr.getBytes())}); + // 4. a ROUND-CHANGE without a prepared claim, sealed by its own index + final Bytes32 rm = PqAnchor.roundChangeMessage(CHAIN, H_FIN, 2); + final RoundChange r1 = mf.createRoundChange(new ConsensusRoundIdentifier(H_FIN, 2), Optional.empty(), seal(falcon, 2, 2, rm)); + assertThat(r1.getAuthor()).isEqualTo(ids.get(2)); + out.add(new String[] {"roundchange-own-seal", "roundchange", Long.toString(H_FIN), hex(r1.encode()), hex(ids.get(2).getBytes())}); + // 5. a ROUND-CHANGE sealed over another target round: no author + final RoundChange r2 = + mf.createRoundChange( + new ConsensusRoundIdentifier(H_FIN, 2), Optional.empty(), + seal(falcon, 2, 2, PqAnchor.roundChangeMessage(CHAIN, H_FIN, 3))); + assertThat(r2.getAuthor()).isEqualTo(AerePqFinality.NO_AUTHOR); + out.add(new String[] {"roundchange-other-round", "roundchange", Long.toString(H_FIN), hex(r2.encode()), hex(AerePqFinality.NO_AUTHOR.getBytes())}); + // 6. a COMMIT naming index 3 with validator 3's commit seal: index 3's id + final SECPSignature own = ecdsa.get(3).sign(Bytes32.wrap(digest.getBytes())); + final Commit c1 = mf.createCommit(new ConsensusRoundIdentifier(H_FIN, 0), digest, own, Optional.of(new FalconSeal(3, Bytes.of(1)))); + assertThat(c1.getAuthor()).isEqualTo(ids.get(3)); + out.add(new String[] {"commit-own-commit-seal", "commit", Long.toString(H_FIN), hex(c1.encode()), hex(ids.get(3).getBytes())}); + // 7. a COMMIT naming index 3 with validator 0's commit seal: no author + final SECPSignature other = ecdsa.get(0).sign(Bytes32.wrap(digest.getBytes())); + final Commit c2 = mf.createCommit(new ConsensusRoundIdentifier(H_FIN, 0), digest, other, Optional.of(new FalconSeal(3, Bytes.of(1)))); + assertThat(c2.getAuthor()).isEqualTo(AerePqFinality.NO_AUTHOR); + out.add(new String[] {"commit-another-commit-seal", "commit", Long.toString(H_FIN), hex(c2.encode()), hex(AerePqFinality.NO_AUTHOR.getBytes())}); + + final StringBuilder j = new StringBuilder(); + j.append("{\n \"note\": \"AIP-22 stage 4 shared authorship vectors, written by AereFinalityVectorsWriter (Besu-derived client); PROBE keys\",\n"); + j.append(" \"chainId\": ").append(CHAIN).append(",\n \"bindHeight\": ").append(BIND) + .append(",\n \"identityBlock\": ").append(H_ID).append(",\n \"keysFrom\": ").append(H_ID) + .append(",\n \"finalityBlock\": ").append(H_FIN).append(",\n \"rows\": [\n"); + for (int i = 0; i < N; i++) { + j.append(" {\"index\": ").append(i).append(", \"id\": \"").append(hex(ids.get(i).getBytes())) + .append("\", \"falcon\": \"").append(hex(Bytes.wrap(std.get(i)))) + .append("\", \"ecdsa\": \"").append(hex(Util.publicKeyToAddress(ecdsa.get(i).getPublicKey()).getBytes())) + .append("\"}").append(i + 1 < N ? ",\n" : "\n"); + } + j.append(" ],\n \"messages\": [\n"); + for (int k = 0; k < out.size(); k++) { + final String[] m = out.get(k); + j.append(" {\"name\": \"").append(m[0]).append("\", \"type\": \"").append(m[1]).append("\", \"height\": ").append(m[2]) + .append(", \"wire\": \"").append(m[3]).append("\", \"author\": \"").append(m[4]).append("\"}") + .append(k + 1 < out.size() ? ",\n" : "\n"); + } + j.append(" ]\n}\n"); + final Path dir = Path.of(System.getenv("AERE_T4_VECTORS_OUT")); + Files.createDirectories(dir); + Files.writeString(dir.resolve("author-t4.json"), j.toString(), StandardCharsets.UTF_8); + } + + private static Optional seal( + final List falcon, final int index, final int signer, final Bytes32 message) { + return Optional.of( + new FalconSeal(index, Bytes.wrap(FALCON.sign(falcon.get(signer), message.toArrayUnsafe()).orElseThrow()))); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereIdentityMessageTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereIdentityMessageTest.java new file mode 100644 index 0000000000..9124dc9338 --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/AereIdentityMessageTest.java @@ -0,0 +1,165 @@ +/* + * AERE AIP-22 stage 3: the identity switch, measured on real QBFT messages. + * + * Four upstream test nodes (QbftNodeList: a secp256k1 node key and a message factory each) get a + * registry v3 row each (a Falcon-512 key with its possession proof, and the ecdsa link signed by the + * node's own key). The validator set given to the upstream validators is the list of their IDS. + * + * What must hold, each with its negative: + * - armed, a Commit (and a Prepare) signed with the node's ECDSA key is accepted against the id set: + * the author is translated to the id, and the commit seal's creator is translated the same way; + * - DISARMED, the very same message is REFUSED against the id set (its author is an address): the + * translation, and nothing else, is what makes it pass; + * - armed but BELOW H_id, it is refused against ids and accepted against addresses; + * - a node with no row stays an address and is refused. + */ +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.ConsensusRoundIdentifier; +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.core.messagewrappers.Commit; +import org.hyperledger.besu.consensus.qbft.core.messagewrappers.Prepare; +import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCodec; +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.crypto.SecureRandomProvider; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +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; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AereIdentityMessageTest { + + private static final long CHAIN = 28001; + private static final long H_ID = 10; + private static final int N = 4; + + private final Hash expectedHash = Hash.fromHexStringLenient("0x1"); + private @Mock QbftBlockCodec qbftBlockCodec; + private QbftNodeList nodes; + private final List
    ids = new ArrayList<>(); + + @BeforeEach + public void registryForTheNodes() { + nodes = QbftNodeList.createNodes(N, qbftBlockCodec); + final long bind = 5; + final List rows = new ArrayList<>(); + for (int i = 0; i < N; i++) { + final SealScheme f = SealSchemes.FALCON_512; + final SealScheme.GeneratedPair p = f.generate(SecureRandomProvider.createSecureRandom()); + 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, f.wireId(), std); + final byte[] pop = f.sign(p.privateKey(), popMsg.toArrayUnsafe()).orElseThrow(); + final TreeMap keys = new TreeMap<>(); + keys.put(f.wireId() & 0xff, new PqRegistryV3.Key(f, std, pop)); + final PqRegistryV3.Row bare = new PqRegistryV3.Row(i, id, keys, null, Map.of(), null, null); + final Bytes32 rh = PqRegistryV3.rowHash(CHAIN, bind, N, bare); + final SECPSignature claim = + nodes.getNode(i).getNodeKey().sign(PqRegistryV3.claimDigest(CHAIN, rh)); + rows.add( + new PqRegistryV3.Row( + i, id, keys, null, Map.of(), + nodes.getNode(i).getAddress().getBytes().toArrayUnsafe(), + claim.encodedBytes().toArrayUnsafe())); + ids.add(Address.wrap(Bytes.wrap(id))); + } + final List hashes = new ArrayList<>(); + for (final PqRegistryV3.Row r : rows) { + hashes.add(PqRegistryV3.rowHash(CHAIN, bind, N, r)); + } + final Bytes32 root = PqRegistryV3.merkleRoot(hashes); + final PqRegistryV3.Registry reg = + new PqRegistryV3.Registry( + CHAIN, bind, N, rows, hashes, root, PqRegistryV3.registryHash(CHAIN, bind, N, root)); + final TreeMap epochs = new TreeMap<>(); + epochs.put(bind, reg); + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs)); + } + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + private Commit commit(final int node, final ConsensusRoundIdentifier round) { + final SECPSignature seal = + nodes.getNode(node).getNodeKey().sign(Bytes32.wrap(expectedHash.getBytes())); + return nodes.getMessageFactory(node).createCommit(round, expectedHash, seal); + } + + @Test + public void armedACommitSignedByTheNodeKeyIsAcceptedAgainstTheIdSet() { + final ConsensusRoundIdentifier round = new ConsensusRoundIdentifier(H_ID, 0); + final CommitValidator v = new CommitValidator(ids, round, expectedHash, expectedHash); + for (int i = 0; i < N; i++) { + final Commit c = commit(i, round); + assertThat(c.getAuthor()).isEqualTo(ids.get(i)); + assertThat(v.validate(c)).as("node " + i).isTrue(); + } + } + + @Test + public void disarmedTheSameCommitIsRefusedAgainstTheIdSet() { + final ConsensusRoundIdentifier round = new ConsensusRoundIdentifier(H_ID, 0); + AerePqIdentity.uninstall(); + final CommitValidator v = new CommitValidator(ids, round, expectedHash, expectedHash); + final Commit c = commit(0, round); + assertThat(c.getAuthor()).isEqualTo(nodes.getNode(0).getAddress()); + assertThat(v.validate(c)).isFalse(); + } + + @Test + public void belowHidTheAuthorIsTheAddress() { + final ConsensusRoundIdentifier round = new ConsensusRoundIdentifier(H_ID - 1, 0); + final Commit c = commit(1, round); + assertThat(c.getAuthor()).isEqualTo(nodes.getNode(1).getAddress()); + assertThat(new CommitValidator(ids, round, expectedHash, expectedHash).validate(c)).isFalse(); + assertThat( + new CommitValidator(nodes.getNodeAddresses(), round, expectedHash, expectedHash) + .validate(c)) + .isTrue(); + } + + @Test + public void aNodeWithoutARowStaysAnAddressAndIsRefused() { + final ConsensusRoundIdentifier round = new ConsensusRoundIdentifier(H_ID + 3, 0); + final QbftNode stranger = QbftNode.create(qbftBlockCodec); + final SECPSignature seal = stranger.getNodeKey().sign(Bytes32.wrap(expectedHash.getBytes())); + final Commit c = stranger.getMessageFactory().createCommit(round, expectedHash, seal); + assertThat(c.getAuthor()).isEqualTo(stranger.getAddress()); + assertThat(new CommitValidator(ids, round, expectedHash, expectedHash).validate(c)).isFalse(); + } + + @Test + public void armedAPrepareIsAuthoredByTheId() { + final ConsensusRoundIdentifier round = new ConsensusRoundIdentifier(H_ID + 1, 0); + for (int i = 0; i < N; i++) { + final Prepare p = nodes.getMessageFactory(i).createPrepare(round, expectedHash); + assertThat(p.getAuthor()).isEqualTo(ids.get(i)); + assertThat(new PrepareValidator(ids, round, expectedHash).validate(p)).isTrue(); + } + AerePqIdentity.uninstall(); + final Prepare p = nodes.getMessageFactory(2).createPrepare(round, expectedHash); + assertThat(new PrepareValidator(ids, round, expectedHash).validate(p)).isFalse(); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java new file mode 100644 index 0000000000..fffe7b61ba --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqFinalityLayersGuardTest.java @@ -0,0 +1,130 @@ +/* + * 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.core.validation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +/** + * AIP-22 stage 4: H_fin is refused over a seal layer that is not verified from H_fin (AERE-PQC-FIN-02), + * named layer by layer; H_fin unset is inert. The last probe goes through the production path (the + * four properties the layers really read). The runner's negative control plants "never refuse" at run + * time and these probes turn red. + */ +public class PqFinalityLayersGuardTest { + + private static final long H_FIN = 5_000L; + private static final String[] PROPS = { + PqProposalEnforcement.PROPERTY_FORK_BLOCK, + PqPrepareEnforcement.PROPERTY_FORK_BLOCK, + PqCommitEnforcement.PROPERTY_FORK_BLOCK, + PqRoundChangeEnforcement.PROPERTY_FORK_BLOCK + }; + + @AfterEach + public void clear() { + for (final String p : PROPS) { + System.clearProperty(p); + } + } + + private static PqFinalityLayersGuard.Layer armedFrom(final String property, final long from) { + return new PqFinalityLayersGuard.Layer(property, h -> h >= from); + } + + private static List allArmedFrom(final long from) { + return List.of( + armedFrom(PROPS[0], from), armedFrom(PROPS[1], from), armedFrom(PROPS[2], from), + armedFrom(PROPS[3], from)); + } + + @Test + public void everyLayerArmedAtOrBelowHfinPassesAndHfinUnsetIsInert() { + assertThat(PqFinalityLayersGuard.refusal(H_FIN, allArmedFrom(H_FIN))).isEmpty(); + assertThat(PqFinalityLayersGuard.refusal(H_FIN, allArmedFrom(100))).isEmpty(); + final List none = + List.of( + new PqFinalityLayersGuard.Layer(PROPS[0], null), + new PqFinalityLayersGuard.Layer(PROPS[1], null), + new PqFinalityLayersGuard.Layer(PROPS[2], null), + new PqFinalityLayersGuard.Layer(PROPS[3], null)); + assertThat(PqFinalityLayersGuard.refusal(AerePqIdentity.NEVER, none)).isEmpty(); + } + + @Test + public void aLayerUnsetOrArmedAboveHfinIsRefusedByName() { + for (int i = 0; i < PROPS.length; i++) { + final List unset = new java.util.ArrayList<>(allArmedFrom(100)); + unset.set(i, new PqFinalityLayersGuard.Layer(PROPS[i], null)); + final Optional r = PqFinalityLayersGuard.refusal(H_FIN, unset); + assertThat(r).as("layer %s unset", PROPS[i]).isPresent(); + assertThat(r.get()).startsWith("AERE-PQC-FIN-02").contains(PROPS[i] + " unset"); + + final List late = new java.util.ArrayList<>(allArmedFrom(100)); + late.set(i, armedFrom(PROPS[i], H_FIN + 1)); + final Optional r2 = PqFinalityLayersGuard.refusal(H_FIN, late); + assertThat(r2).as("layer %s armed one block above H_fin", PROPS[i]).isPresent(); + assertThat(r2.get()).contains(PROPS[i] + " armed above " + H_FIN); + } + } + + @Test + public void productionPathReadsTheFourPropertiesTheLayersRead() { + for (final String p : PROPS) { + System.setProperty(p, Long.toString(H_FIN - 10)); + } + PqFinalityLayersGuard.verifyOrAbort(H_FIN); + System.clearProperty(PqRoundChangeEnforcement.PROPERTY_FORK_BLOCK); + assertThatThrownBy(() -> PqFinalityLayersGuard.verifyOrAbort(H_FIN)) + .isInstanceOf(IllegalStateException.class) + .hasMessageStartingWith("AERE-PQC-FIN-02") + .hasMessageContaining(PqRoundChangeEnforcement.PROPERTY_FORK_BLOCK + " unset"); + // H_fin unset: inert whatever the layers + PqFinalityLayersGuard.verifyOrAbort(AerePqIdentity.NEVER); + } + + @Test + public void aSealingNodeMustSealAllFourMessagesAtOrBelowHfin() { + final long never = Long.MAX_VALUE; + assertThat(PqFinalityLayersGuard.emissionRefusal(AerePqIdentity.NEVER, true, never, never, never, never)) + .as("unset H_fin asks nothing") + .isEmpty(); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, false, never, never, never, never)) + .as("a reader seals nothing and needs nothing") + .isEmpty(); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, 32, 200, 200, 200)).isEmpty(); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, H_FIN, H_FIN, H_FIN, H_FIN)).isEmpty(); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, 32, never, 200, 200)) + .hasValueSatisfying( + r -> assertThat(r).startsWith("AERE-PQC-FIN-03").contains("aere.pq.proposalPq.attachBlock unset")); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, 32, 200, H_FIN + 1, 200)) + .hasValueSatisfying(r -> assertThat(r).contains("aere.pq.preparePq.attachBlock armed above H_fin")); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, 32, 200, 200, never)) + .hasValueSatisfying(r -> assertThat(r).contains("aere.pq.roundChangePq.attachBlock unset")); + assertThat(PqFinalityLayersGuard.emissionRefusal(H_FIN, true, H_FIN + 5, 200, 200, 200)) + .hasValueSatisfying(r -> assertThat(r).contains("aere.falcon.attachBlock armed above H_fin")); + assertThatThrownBy(() -> PqFinalityLayersGuard.verifyEmissionOrAbort(H_FIN, true, 32, never, 200, 200)) + .isInstanceOf(IllegalStateException.class) + .hasMessageStartingWith("AERE-PQC-FIN-03"); + } +} diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java index cf8c683f01..95b4b667b5 100755 --- a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridEnforcementTest.java @@ -173,40 +173,79 @@ public class PqHybridEnforcementTest { final Optional refusal = hibrid() .refusal(H_HIBRID, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of()); assertThat(refusal).isPresent(); assertThat(refusal.get()).contains("carries no").contains(SealSchemes.SLH_DSA_128S.id()); } @Test public void aFullHybridCertificateCounts() { assertThat( hibrid() .refusal( H_HIBRID, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of(sigiliuSlh(0, DIGEST)))) .isEmpty(); } + // ============================================ intervale per schema (AIP-22 treapta 2) + + /** + * With SLH-DSA on the 128 grid (Falcon on the 32 grid), counted from an activation height of 64, + * a commit at the parent of a Falcon-only anchor counts with the Falcon seal alone, and a commit + * at the parent of a hybrid anchor does not. The two heights differ in nothing but the grid; the + * grid origin is the negative control (read against another origin, the two heights swap). + */ + @Test + public void withPerSchemeIntervalsTheSlhSealIsDemandedOnlyAtParentsOfAnchorsOnItsGrid() { + final PqSchemeSchedule cuIntervale = + PqSchemeSchedule.parse( + H_ARMARE + ":" + SealSchemes.FALCON_512.id() + "/32+" + SealSchemes.SLH_DSA_128S.id() + "/128"); + final long origine = 64L; + long ancoraHibrida = origine; + while (ancoraHibrida <= H_HIBRID) { + ancoraHibrida += 128L; + } + final long ancoraDoarFalcon = ancoraHibrida + 32L; + final PqCommitEnforcement e = + new PqCommitEnforcement(H_ARMARE, registruFalcon(), cuIntervale, registry, h -> true, () -> origine); + assertThat(e.refusal(ancoraDoarFalcon - 1, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of())) + .isEmpty(); + final Optional refusal = + e.refusal(ancoraHibrida - 1, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of()); + assertThat(refusal).isPresent(); + assertThat(refusal.get()).contains("carries no").contains(SealSchemes.SLH_DSA_128S.id()); + assertThat( + e.refusal( + ancoraHibrida - 1, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of(sigiliuSlh(0, DIGEST)))) + .isEmpty(); + final PqCommitEnforcement altaOrigine = + new PqCommitEnforcement(H_ARMARE, registruFalcon(), cuIntervale, registry, h -> true, () -> origine + 32L); + assertThat(altaOrigine.refusal(ancoraHibrida - 1, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of())) + .isEmpty(); + assertThat(altaOrigine.refusal(ancoraDoarFalcon - 1, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of())) + .isPresent(); + } + // ============================================================ controalele negative @Test public void aHybridSealOverAnotherDigestDoesNotCount() { final Hash altul = Hash.hash(Bytes.of(9, 9)); final Optional refusal = hibrid() .refusal( H_HIBRID, VALIDATOR_0, DIGEST, Optional.of(sigiliuFalcon(0)), List.of(sigiliuSlh(0, altul))); assertThat(refusal).isPresent(); assertThat(refusal.get()).contains("does NOT verify"); } @Test public void aHybridSealForAnotherIndexIsRefused() { final Optional refusal = diff --git a/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridRotationV3Test.java b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridRotationV3Test.java new file mode 100644 index 0000000000..3a3e69691a --- /dev/null +++ b/consensus/qbft-core/src/test/java/org/hyperledger/besu/consensus/qbft/core/validation/PqHybridRotationV3Test.java @@ -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 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 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() { + return Optional.of(new FalconSeal(0, Bytes.wrap(FALCON.sign(falcon0.privateKey(), DIGEST.getBytes().toArray()).orElseThrow()))); + } + + private List 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 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 r = + enforcement.refusal(H_K + 5, Address.fromHexString("0x" + "cd".repeat(20)), DIGEST, falconSeal(), slhSeal(slhNew)); + assertThat(r).isPresent(); + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java index cfc67a900d..4a2c4f9e0b 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactory.java @@ -10,46 +10,47 @@ * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was * found, as section 4(c) requires. The change itself is in patches/0003-aere-pq-anchor.patch. */ package org.hyperledger.besu.consensus.qbft; import static org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification.DEFAULT_MAX_GAS_LIMIT; import static org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification.DEFAULT_MIN_GAS_LIMIT; import org.hyperledger.besu.consensus.common.bft.BftHelpers; import org.hyperledger.besu.consensus.common.bft.FalconSealSupport; 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.BftCommitSealsValidationRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.AereBaseFeeImportRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.FalconSealValidationRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestAttachedRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorDigestRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.PqAnchorSealsRule; +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.PqRegistryBindingRule; import org.hyperledger.besu.consensus.qbft.headervalidationrules.QbftValidatorsValidationRule; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.AncestryValidationRule; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ConstantFieldValidationRule; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasLimitRangeAndDeltaValidationRule; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValidationRule; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent; import java.time.Duration; import java.util.Optional; import org.apache.tuweni.units.bigints.UInt256; /** The Qbft block header validation ruleset factory. */ public class QbftBlockHeaderValidationRulesetFactory { @@ -144,61 +145,67 @@ public class QbftBlockHeaderValidationRulesetFactory { DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT, baseFeeMarket)) .addRule(new TimestampBoundedByFutureParameter(1)) // AERE D-AMONTE-02: the base-fee rule every non-BFT factory in this jar wires and the // QBFT one upstream forgot. Height-gated (disarmed = today's behaviour, byte for byte): // the chain's HISTORY contains blocks this validation would reject (the two floor-less // days, the lost-threshold window where the fee was not a function of the parent), so // it must never look below its arming height. Delegates to THIS node's fee market, so // the AERE 1 Gwei floor is validated too - the check that closes the empty-block // one-wei divergence measured on the mixed network (STARE-PRODUCATOR 1bis). .addRule( new AereBaseFeeImportRule( AereBaseFeeImportRule.armedFromSystemConfig(), baseFeeMarket)) .addRule( new ConstantFieldValidationRule<>( "MixHash", BlockHeader::getMixHash, BftHelpers.EXPECTED_MIX_HASH)) .addRule( new ConstantFieldValidationRule<>( "Difficulty", BlockHeader::getDifficulty, UInt256.ONE)) .addRule(new QbftValidatorsValidationRule(useValidatorContract)) .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 // unconfigured anchor this is Long.MAX_VALUE, i.e. it never stands down and behaviour is // unchanged. .addRule( new FalconSealValidationRule(pqAnchorConfig.legacyFalconRuleRetirementBlock())) // AERE ANCORA-V2 R1: cheap, detached, IN LIGHT VALIDATION. Ordered before R2 so the one // keccak that binds the certificate to the block hash runs before k Falcon // verifications are spent on it. .addRule(new PqAnchorDigestRule(pqAnchorConfig)) // AERE SINCRONIZARE R1A (2026-08-02): the SAME digest verdict, on the ATTACHED side. // Measured cause it repairs: SKIP_DETACHED, the mode FullImportBlockStep passes to // importBlock for every block acquired by sync, keeps exactly the rules that are NOT // detached, so R1 does not run at import; and DETACHED_ONLY, the mode under which R1 // does run, is only applied by DownloadHeaderSequenceTask, i.e. only on ranges with a // closed end. The open-ended range at the tip of a recovery, 199 blocks by default, was // therefore checked by neither. Ordered immediately before R2 so the one keccak still // runs before k Falcon verifications are spent. Delegates to R1, so there is one // definition of the digest and the two paths cannot drift. .addRule(new PqAnchorDigestAttachedRule(pqAnchorConfig)) // AERE ANCORA-V2 R2: attached, full validation only. .addRule(new PqAnchorSealsRule(pqAnchorConfig)) // AERE A8 per-block half: the registry this node runs must be the registry // config.pqRegistryHash requires AT THIS HEIGHT. The startup guard answers // that once, against the head that existed at startup; a rotation entry in // the schedule can pass underneath a running node and never be noticed. // Inert when no schedule is configured, which is chain 2800 today. - .addRule(new PqRegistryBindingRule()); + .addRule(new PqRegistryBindingRule()) + // AERE AIP-22 stage 4: the checkpoint (aere.pq.checkpoint=:); a header at a + // checkpoint height with another hash is refused. Attached, so it runs at import. Inert + // when unset, which is every chain today; a malformed value refuses at load. + .addRule(PqCheckpointRule.fromSystemConfiguration()); // Currently the minimum acceptable time between blocks is 1 second. The timestamp of an // Ethereum header is stored as seconds since Unix epoch so blocks being produced more // frequently than once a second cannot pass this validator. For non-production scenarios // (e.g. for testing block production much more frequently than once a second) Besu has // an experimental 'xblockperiodmilliseconds' option for BFT chains. If this is enabled // we cannot apply the TimestampMoreRecentThanParent validation rule so we do not add it if (minimumTimeBetweenBlocks.compareTo(Duration.ofSeconds(1)) >= 0) { ruleBuilder.addRule(new TimestampMoreRecentThanParent(minimumTimeBetweenBlocks.toSeconds())); } return ruleBuilder; } } diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java index b3096d3313..d4f6e2d8fc 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftExtraDataCodec.java @@ -89,40 +89,53 @@ public class QbftExtraDataCodec extends BftExtraDataCodec { /** * Create genesis extra data string. * * @param validators the validators * @return the string */ public static String createGenesisExtraDataString(final List
    validators) { return encodeFromAddresses(validators).toString(); } @Override public BftExtraData decodeRaw(final Bytes input) { if (input.isEmpty()) { throw new IllegalArgumentException("Invalid Bytes supplied - Bft Extra Data required."); } final RLPInput rlpInput = new BytesValueRLPInput(input, false); rlpInput.enterList(); // This accounts for the "root node" which contains BFT data items. final Bytes vanityData = rlpInput.readBytes(); + // AERE-D415 (2026-09-25): the vanity field is exactly 32 bytes at EVERY height, not only where + // the anchor rule looks (PqAnchorDigestRule checks it at anchor heights alone). The second + // client's decoder has required 32 from the start, so every header of chains 2800 and 28001, + // both geneses included, satisfies it; the differential fuzzer of the stage-5 header rule found + // the two clients judging a 0-, 31-, 33- or 64-byte vanity between anchors differently (this + // codec admitted it, the second client refused it). One rule, in the codec, for both clients. + if (vanityData.size() != EXTRA_VANITY_LENGTH) { + throw new RLPException( + "AERE-D415: QBFT vanity must be exactly " + + EXTRA_VANITY_LENGTH + + " bytes, the header carries " + + vanityData.size()); + } final List
    validators = rlpInput.readList(Address::readFrom); final Optional vote; if (rlpInput.nextIsList() && rlpInput.nextSize() == 0) { vote = Optional.empty(); rlpInput.skipNext(); } else { vote = Optional.of(decodeVote(rlpInput)); } final int round = rlpInput.readIntScalar(); final List seals = rlpInput.readList( rlp -> SignatureAlgorithmFactory.getInstance().decodeSignature(rlp.readBytes())); // AERE hybrid PQC: an OPTIONAL parallel Falcon-512 seal list may follow the ECDSA seals. // Backward-tolerant: older/exclude/no-certificate encodings without this element decode to an // empty list. final List falconSeals; final List hybridSeals; @@ -147,54 +160,69 @@ public class QbftExtraDataCodec extends BftExtraDataCodec { + e.getMessage()); } } else { hybridSeals = Collections.emptyList(); falconSeals = new BytesValueRLPInput(certificateRaw, false) .readList( rlp -> { rlp.enterList(); final int idx = rlp.readIntScalar(); final Bytes sig = rlp.readBytes(); rlp.leaveList(); return new FalconSeal(idx, sig); }); } } else { falconSeals = 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 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. // // Upstream ends this decode with leaveListLenient(), which silently discards anything still // left in the outer list. That is not survivable here, because the QBFT block hash and the // committed-seal pre-image are BOTH computed from a RE-ENCODE of the decoded BftExtraData // (BftBlockHashing.calculateHashOfBftBlockOnchain line 84-91, calculateDataHashForCommittedSeal // line 53-75). Anything the decoder discards is therefore dropped before hashing, so two // different header byte strings carry the same block hash and the same genuine validator // seals. leaveList() rejects exactly the inputs that have content left over, and is // byte-for-byte identical to the lenient leave on every input that has not. rlpInput.leaveList(); 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. // // The strict leave alone does not close the hole. The Falcon element is OPTIONAL on the wire // and the encoder omits it when the certificate is empty, so a header carrying an explicit // EMPTY Falcon list decodes to exactly the same value as a header carrying no Falcon element // at all: the trailing 0xc0 is CONSUMED, not left over, so no leave, strict or lenient, can // see it. Requiring the full re-encode to reproduce the received bytes makes the accepted // encoding unique by construction, and closes every present and future shape of this defect // rather than the one instance of it we happened to look for. // // Backward compatibility: the re-encode used here is the same EncodingType.ALL the block // producer uses, so every header any Aere or stock Besu node has ever produced round trips // unchanged. Headers with no Falcon element re-encode with no Falcon element, which is why // the whole pre-Falcon history of the chain still decodes. final Bytes canonical = encode(decoded, EncodingType.ALL); if (!canonical.equals(input)) { throw new RLPException( "Non-canonical BFT extra data: the header's extraData is " + input.size() @@ -226,40 +254,50 @@ public class QbftExtraDataCodec extends BftExtraDataCodec { if (encodingType != EncodingType.EXCLUDE_COMMIT_SEALS) { encoder.writeList( bftExtraData.getSeals(), (committer, rlp) -> rlp.writeBytes(committer.encodedBytes())); // AERE hybrid PQC: only the full (stored-header) encoding carries the parallel Falcon // quorum certificate, and only when it is non-empty. The exclude encodings omit it so the // committed-seal hash and the on-chain block hash are byte-identical to upstream Besu, and a // header with no certificate is byte-identical to upstream too, so the Falcon seals are // never part of any signed pre-image and never disturb the block hash. if (!bftExtraData.getHybridSeals().isEmpty()) { // AERE ANCHOR V2: the scheme-tagged certificate, canonical bytes, in the same slot. encoder.writeRaw(PqAnchorV2.encode(bftExtraData.getHybridSeals())); } else if (!bftExtraData.getFalconSeals().isEmpty()) { encoder.writeList( bftExtraData.getFalconSeals(), (falconSeal, rlp) -> { rlp.startList(); rlp.writeIntScalar(falconSeal.getValidatorIndex()); rlp.writeBytes(falconSeal.getSignature()); 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 { encoder.writeEmptyList(); } } else { encoder.writeIntScalar(0); encoder.writeEmptyList(); } encoder.endList(); return encoder.encoded(); } /** * Encode vote. * * @param rlpOutput the rlp output * @param vote the vote */ protected void encodeVote(final RLPOutput rlpOutput, final Vote vote) { diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java index dd67707b14..24769c3472 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftBlockCreatorAdaptor.java @@ -70,40 +70,72 @@ public class QbftBlockCreatorAdaptor implements QbftBlockCreator { this.besuBlockCreator = besuBftBlockCreator; this.bftExtraDataCodec = bftExtraDataCodec; } @Override public BlockCreationResult createBlock( final long headerTimeStampSeconds, final QbftBlockHeader parentHeader) { var blockResult = besuBlockCreator.createBlock( headerTimeStampSeconds, AdaptorUtil.toBesuBlockHeader(parentHeader)); return new BlockCreationResult( new QbftBlockAdaptor(blockResult.getBlock()), blockResult.getBlockAccessList()); } @Override public QbftBlock createSealedBlock( final QbftBlock block, final int roundNumber, final Collection commitSeals) { 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 public QbftBlock createSealedBlock( final QbftBlock block, final int roundNumber, final Collection commitSeals, final Collection collectedFalconSeals) { final Block besuBlock = AdaptorUtil.toBesuBlock(block); final QbftBlockHeader initialHeader = block.getHeader(); final BftExtraData initialExtraData = bftExtraDataCodec.decode(AdaptorUtil.toBesuBlockHeader(initialHeader)); // AERE ANCHOR V2, THE MEASURED TRAP, CLOSED HERE. Above the activation height element 6 is no // longer "the seals I heard for THIS block": it is the quorum certificate over the PARENT, // CHOSEN BY THE PROPOSER, and vanityData carries its digest D. Every node that seals this block // must therefore carry the proposer's bytes across UNCHANGED. The legacy assembly below would // overwrite them with what this node heard for the current block, which would leave a header // whose vanity D no longer matches its own element 6, i.e. a header this node's own digest rule // rejects. So above the activation height the assembler is skipped outright. // // Note what is NOT done here: no re-verification, no re-selection, no comparison against what diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftFinalStateImpl.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftFinalStateImpl.java index 5a3ff17130..03d2c64c9c 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftFinalStateImpl.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/adaptor/QbftFinalStateImpl.java @@ -1,36 +1,43 @@ /* * Copyright contributors to Besu. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.consensus.qbft.adaptor; +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.BftHelpers; import org.hyperledger.besu.consensus.common.bft.BlockTimer; import org.hyperledger.besu.consensus.common.bft.ConsensusRoundIdentifier; import org.hyperledger.besu.consensus.common.bft.RoundTimer; import org.hyperledger.besu.consensus.common.bft.blockcreation.ProposerSelector; import org.hyperledger.besu.consensus.common.bft.network.ValidatorMulticaster; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.consensus.qbft.core.types.QbftBlockCreatorFactory; import org.hyperledger.besu.consensus.qbft.core.types.QbftFinalState; import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.datatypes.Address; import java.time.Clock; import java.util.Collection; /** Besu implementation of QbftFinalState for maintaining the state of a QBFT network. */ public class QbftFinalStateImpl implements QbftFinalState { private final ValidatorProvider validatorProvider; private final NodeKey nodeKey; private final Address localAddress; @@ -105,82 +112,83 @@ public class QbftFinalStateImpl implements QbftFinalState { return nodeKey; } /** * Gets local address. * * @return the local address */ @Override public Address getLocalAddress() { return localAddress; } /** * Is local node validator. * * @return the boolean */ @Override public boolean isLocalNodeValidator() { - return getValidators().contains(localAddress); + return getValidators().stream().anyMatch(a -> AerePqIdentity.isLocal(a, localAddress)); } /** * Gets round timer. * * @return the round timer */ @Override public RoundTimer getRoundTimer() { return roundTimer; } /** * Gets block creator factory. * * @return the block creator factory */ @Override public QbftBlockCreatorFactory getBlockCreatorFactory() { return blockCreatorFactory; } @Override public int getQuorum() { return BftHelpers.calculateRequiredValidatorQuorum(getValidators().size()); } @Override public BlockTimer getBlockTimer() { return blockTimer; } /** * Is local node proposer for round. * * @param roundIdentifier the round identifier * @return the boolean */ @Override public boolean isLocalNodeProposerForRound(final ConsensusRoundIdentifier roundIdentifier) { - return getProposerForRound(roundIdentifier).equals(localAddress); + return getProposerForRound(roundIdentifier) + .equals(AerePqIdentity.local(localAddress, roundIdentifier.getSequenceNumber())); } /** * Gets proposer for round. * * @param roundIdentifier the round identifier * @return the proposer for round */ public Address getProposerForRound(final ConsensusRoundIdentifier roundIdentifier) { return proposerSelector.selectProposerForRound(roundIdentifier); } /** * Gets clock. * * @return the clock */ @Override public Clock getClock() { return clock; diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDigestRule.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDigestRule.java old mode 100755 new mode 100644 index 3b80c4302c..a9ba7a2ef3 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDigestRule.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDigestRule.java @@ -119,79 +119,79 @@ public class PqAnchorDigestRule implements DetachedBlockHeaderValidationRule { public boolean validate(final BlockHeader header, final BlockHeader parent) { final long number = header.getNumber(); // HEIGHT GATE FIRST. Below H nothing at all happens: no decode, no allocation, no log. This is // the property that lets a binary built from this code run on a live node before activation and // behave bit-for-bit as today. From 2026-08-07 the gate also covers the interval: at a height // that carries no certificate there is nothing to bind, so there is nothing to judge. // // THE SAME METHOD AS R2, and not two questions that merely resemble each other. See the note in // PqAnchorSealsRule. if (!config.anchorAppliesAt(number)) { return true; } // SECOND GATE: the named historical windows in which these rules were not in force. Inside one // of them vanityData carries the client's version string rather than a digest, so there is no // digest to match and no recomputation can produce one; refusing would stop this node forever // at a height the whole network holds. See PqAnchorLapse for what happened, how the bounds were // measured, and why an exception written anywhere other than validation moves the error instead // of ending it. - if (PqAnchorLapse.isDisarmed(number)) { + if (PqAnchorLapse.isDisarmed(config.chainId(), number)) { return acceptAsWrittenInsideLapse(header, number); } try { final BftExtraData extraData = extraDataCodec.decodeRaw(header.getExtraData()); final Bytes vanity = extraData.getVanityData(); if (vanity.size() != BftExtraDataCodec.EXTRA_VANITY_LENGTH) { LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - vanityData is {} bytes, expected {}. From H " + "the whole vanity field is the anchor digest D.", number, vanity.size(), BftExtraDataCodec.EXTRA_VANITY_LENGTH); return false; } // AERE ANCHOR V2 (2026-09-03): from the v2 height the ONLY accepted form is the scheme-tagged // certificate under the v2 digest; below it a v2 certificate is refused. Both directions // are checked so the switch is a clean fork and never a header two nodes read differently. if (config.anchorV2AppliesAt(number)) { return judgeV2(number, header, extraData, vanity); } if (!extraData.getHybridSeals().isEmpty()) { LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - carries a v2 (scheme-tagged) certificate below" + " the v2 activation height {}.", number, config.anchorV2Block()); return false; } final List certificate = List.copyOf(extraData.getFalconSeals()); // Height aware since 2026-08-11. Inside the one measured historical window the ordering // requirement relaxes from SORTED to DISTINCT, because 36 canonical headers were written // unsorted during a production interruption and a node syncing from genesis would otherwise // stop at the first of them and never pass it. Distinctness is not relaxed at any height, so a // repeated index stays unrepresentable everywhere. See PqAnchor.hasAcceptableIndices. - if (!PqAnchor.hasAcceptableIndices(certificate, number)) { + if (!PqAnchor.hasAcceptableIndices(config.chainId(), certificate, number)) { // Checked HERE, not only in the seals rule, so reordering and duplicate indices are caught // in LIGHT validation too, and so that "the order given" and "sorted" are the same list for // every header this rule accepts. LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - the carried certificate's validator indices " + "are not strictly increasing ({} seal(s)). Exactly one order is accepted for a " + "given set, which is also what makes a repeated index unrepresentable.", number, certificate.size()); return false; } final Bytes32 expected = PqAnchor.anchorDigest( config.chainId(), number - 1L, header.getParentHash().getBytes(), certificate); if (!expected.equals(Bytes32.wrap(vanity))) { LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - anchor digest mismatch. vanityData carries " + "{} but the certificate actually carried in this header ({} seal(s), over parent " @@ -260,48 +260,48 @@ public class PqAnchorDigestRule implements DetachedBlockHeaderValidationRule { * history that exists and nothing weaker. * *

    Decoding must still succeed. A header this rule cannot parse has not been shown to be one of * the headers the window is about, so it is refused here as everywhere else in this file. * * @param header the header being judged * @param number its height * @return true iff the carried certificate decodes and its indices are distinct and non-negative */ private boolean acceptAsWrittenInsideLapse(final BlockHeader header, final long number) { try { final BftExtraData extraData = extraDataCodec.decodeRaw(header.getExtraData()); final List certificate = List.copyOf(extraData.getFalconSeals()); if (!PqAnchor.hasDistinctNonNegativeIndices(certificate)) { LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - it falls inside the historical range {} in " + "which the anchor rules were not in force, but its certificate repeats a " + "validator index or carries a negative one. That is not a shape any header of " + "that range has, and it is the one property the exception does not relax.", number, - PqAnchorLapse.windowCovering(number).orElse(null)); + PqAnchorLapse.windowCovering(config.chainId(), number).orElse(null)); return false; } LOG.debug( "AERE PQ ANCHOR (R1): block {} accepted as written; it falls inside the historical range " + "{} in which the anchor rules were not in force, so it carries no digest to bind.", number, - PqAnchorLapse.windowCovering(number).orElse(null)); + PqAnchorLapse.windowCovering(config.chainId(), number).orElse(null)); return true; } catch (final Exception e) { LOG.warn( "AERE PQ ANCHOR (R1): block {} REJECTED - it falls inside a historical unenforced range " + "but its extraData could not be decoded: {}", number, e.toString()); return false; } } @Override public boolean includeInLightValidation() { return true; } @Override public String toString() { return "PqAnchorDigest(detached, light, " + (config.everActive() ? "H=" + config.anchorBlock() : "INACTIVE") diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java old mode 100755 new mode 100644 index 78423af47a..3f58217d1f --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorSealsRule.java @@ -1,36 +1,37 @@ /* * 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.AerePqIdentity; import org.hyperledger.besu.consensus.common.bft.BftContext; import org.hyperledger.besu.consensus.common.bft.BftExtraData; import org.hyperledger.besu.consensus.common.bft.FalconSeal; import org.hyperledger.besu.consensus.common.bft.FalconSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSealSupport; import org.hyperledger.besu.consensus.common.bft.HybridSignerRegistry; import org.hyperledger.besu.consensus.common.bft.PqAnchor; import org.hyperledger.besu.consensus.common.bft.PqAnchorConfig; import org.hyperledger.besu.consensus.common.bft.PqAnchorLapse; import org.hyperledger.besu.consensus.common.bft.PqAnchorV2; 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.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; @@ -125,41 +126,41 @@ public class PqAnchorSealsRule implements AttachedBlockHeaderValidationRule { public boolean validate( final BlockHeader header, final BlockHeader parent, final ProtocolContext protocolContext) { final long number = header.getNumber(); // HEIGHT GATE FIRST, same contract as R1: below H this rule does nothing at all, and from // 2026-08-07 it also does nothing at a height that carries no certificate. // // BOTH RULES ASK THE SAME QUESTION, THROUGH THE SAME METHOD, and that is deliberate. // anchorAppliesAt() = activeAt() AND isAnchorHeight(). If R1 and R2 each asked it their own // way, a header accepted by one and refused by the other would be a chain break, appearing at // exactly the first height that is not an anchor height, i.e. the day after activation. if (!config.anchorAppliesAt(number)) { return true; } // SECOND GATE, and the SAME method R1 asks, for the same reason both rules share // anchorAppliesAt: if the two rules each decided this boundary their own way, a header accepted // by one and refused by the other would be a chain break. Inside a named window the threshold // was not in force when the header was written and the indices were written in arrival order, // so neither can be required of it now. - if (PqAnchorLapse.isDisarmed(number)) { + if (PqAnchorLapse.isDisarmed(config.chainId(), number)) { return acceptAsWrittenInsideLapse(header, number); } try { if (parent == null) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - no parent header available, so the " + "certificate cannot be checked against the parent it claims to attest to.", number); return false; } // The certificate attests to the parent by NUMBER and HASH, and R1 has already bound the // digest to header.getParentHash(). If the parent handed to this rule were a different header, // R1 and R2 would be talking about two different objects. Cheap, and it removes any dependence // on rule ordering with AncestryValidationRule. if (!parent.getHash().equals(header.getParentHash()) || parent.getNumber() != number - 1L) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the supplied parent (number {}, hash {}) is " + "not the parent this header names (number {}, hash {}).", @@ -187,66 +188,66 @@ public class PqAnchorSealsRule implements AttachedBlockHeaderValidationRule { } if (!extraData.getHybridSeals().isEmpty()) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - carries a v2 (scheme-tagged) certificate below" + " the v2 activation height {}.", number, config.anchorV2Block()); return false; } final List certificate = List.copyOf(extraData.getFalconSeals()); final int k = certificate.size(); // THE THRESHOLD THIS HEIGHT WAS ACTUALLY JUDGED BY. Normally the configured schedule. Inside // a named historical range it is the LOWER of the schedule and the emergency ceiling that was // in force when these headers were written, which is the same lever the fleet itself used at // the time, replayed from a fixed range instead of from a runtime option. Asking a header for // more than was asked of the proposer that wrote it refuses history that no node can // reproduce; asking for the ceiling rather than for nothing keeps the strongest claim the // range supports, and the ceiling is a measured floor, never zero. See PqAnchorLapse. int required = config.minSealsAt(number); - final OptionalInt inForce = PqAnchorLapse.historicMinSeals(number); + final OptionalInt inForce = PqAnchorLapse.historicMinSeals(config.chainId(), number); if (inForce.isPresent() && inForce.getAsInt() < required) { LOG.debug( "AERE PQ ANCHOR (R2): block {} sits in the historical range {}, so the threshold " + "applied is the {} that was in force then, not the {} the schedule asks for.", number, - PqAnchorLapse.windowCovering(number).orElse(null), + PqAnchorLapse.windowCovering(config.chainId(), number).orElse(null), inForce.getAsInt(), required); required = inForce.getAsInt(); } if (k < required) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the carried certificate has {} seal(s), " + "below the threshold K={} in force at this height.", number, k, required); return false; } // Height aware since 2026-08-11, same measured historical window as rule R1. See // PqAnchor.hasAcceptableIndices for why the exception sits at VALIDATION and nowhere else, and // for what it deliberately does NOT relax. - if (!PqAnchor.hasAcceptableIndices(certificate, number)) { + if (!PqAnchor.hasAcceptableIndices(config.chainId(), certificate, number)) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the certificate's validator indices are not " + "strictly increasing.", number); return false; } if (k == 0) { // Legitimate only while K is 0, which is the warm-up stage that starts at H itself, where // the parent predates the scheme and no certificate can exist. R1 has already checked that // the vanity digest is the digest OF THE EMPTY certificate, so an empty certificate is not // an unchecked one. LOG.debug( "AERE PQ ANCHOR (R2): block {} carries an empty certificate and K={} at this height.", number, required); return true; } final BftContext bftContext = protocolContext.getConsensusContext(BftContext.class); @@ -406,79 +407,98 @@ public class PqAnchorSealsRule implements AttachedBlockHeaderValidationRule { return false; } final BftContext bftContext = protocolContext.getConsensusContext(BftContext.class); final Collection

    parentValidators = bftContext.getValidatorProvider().getValidatorsForBlock(parent); if (parentValidators == null || parentValidators.isEmpty()) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the validator set of the parent block {} could" + " not be resolved, so seal eligibility cannot be decided.", number, parent.getNumber()); return false; } final Set
    eligible = new HashSet<>(parentValidators); final Bytes32 message = PqAnchor.commitMessage(config.chainId(), parent.getNumber(), parent.getHash().getBytes()); if (!falcon.isEmpty() && !falconSealsVerify(number, parent, eligible, message, falcon)) { return false; } final HybridSealSupport hybrid = HybridSealSupport.instance(); + // Two sets, and they differ only with per-scheme intervals (AIP-22 stage 2). NAMED: every scheme + // the step at the parent height names; a seal of any other scheme is refused as today. REQUIRED: + // the named schemes whose grid THIS anchor height sits on, counted from the chain's anchor + // activation height; each must reach K. A seal of a named-but-not-required scheme is a surplus: + // verified and counted like any surplus seal, never ignored, never a reason to refuse. + final Set namedSchemes = new HashSet<>(); final Set requiredSchemes = new HashSet<>(); - hybrid.schedule().ifPresent(sch -> requiredSchemes.addAll(sch.schemesAt(parent.getNumber()))); + hybrid + .schedule() + .ifPresent( + sch -> { + namedSchemes.addAll(sch.schemesAt(parent.getNumber())); + requiredSchemes.addAll(sch.schemesRequiredForAnchor(number, config.anchorBlock())); + }); + namedSchemes.remove(SealSchemes.FALCON_512.id()); requiredSchemes.remove(SealSchemes.FALCON_512.id()); final Map counted = new HashMap<>(); for (final SchemeSeal s : seals) { if (s.getSchemeWireId() == SealSchemes.FALCON_512.wireId()) { continue; } final Optional scheme = SealSchemes.byWireId(s.getSchemeWireId()); - if (scheme.isEmpty() || !requiredSchemes.contains(scheme.get().id())) { + if (scheme.isEmpty() || !namedSchemes.contains(scheme.get().id())) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the v2 certificate carries a seal of scheme 0x{}," + " which the scheme schedule does not name at the parent height {} (named: {}).", number, Integer.toHexString(s.getSchemeWireId() & 0xff), parent.getNumber(), - requiredSchemes); + namedSchemes); return false; } if (!falconIndices.contains(s.getValidatorIndex())) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - a {} seal from index {} is not bound to a Falcon" + " seal of the same index in this certificate.", number, scheme.get().id(), s.getValidatorIndex()); return false; } final Optional registry = hybrid.registry(); - if (registry.isEmpty()) { + // AERE AIP-22 stage 3b: from H_k the key comes from the v3 epoch in force at the PARENT (the height the + // Falcon key of the same certificate is taken at); the hybrid registry is then not the key set any more + final boolean v3Keys = AerePqIdentity.keysFromV3(parent.getNumber()); + if (registry.isEmpty() && !v3Keys) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the certificate carries {} seals and this node" + " has NO hybrid registry loaded, so it cannot verify them. A node that cannot" + " verify has not proved the header good.", number, scheme.get().id()); return false; } - final Optional pk = registry.get().publicKey(s.getValidatorIndex(), scheme.get().id()); + final Optional pk = + v3Keys + ? AerePqIdentity.v3KeyAt(parent.getNumber(), s.getValidatorIndex(), scheme.get().id()) + : registry.get().publicKey(s.getValidatorIndex(), scheme.get().id()); if (pk.isEmpty()) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the hybrid registry holds no {} key for index {}.", number, scheme.get().id(), s.getValidatorIndex()); return false; } if (!scheme.get().verifyRaw(pk.get(), message.toArray(), s.getSignature().toArray())) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - the {} signature from index {} does not verify" + " over M(parent {}).", number, scheme.get().id(), s.getValidatorIndex(), parent.getNumber()); return false; } counted.merge(scheme.get().id(), 1, Integer::sum); } @@ -499,49 +519,49 @@ public class PqAnchorSealsRule implements AttachedBlockHeaderValidationRule { "AERE PQ ANCHOR (R2): block {} accepted with a {}-seal scheme-tagged certificate over parent" + " {} (K={} per scheme, schemes={}).", number, seals.size(), parent.getNumber(), required, requiredSchemes); return true; } private boolean acceptAsWrittenInsideLapse(final BlockHeader header, final long number) { try { final BftExtraData extraData = extraDataCodec.decodeRaw(header.getExtraData()); final List certificate = List.copyOf(extraData.getFalconSeals()); if (!PqAnchor.hasDistinctNonNegativeIndices(certificate)) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - it falls inside the historical range {} in " + "which the anchor rules were not in force, but its certificate repeats a " + "validator index or carries a negative one, which no header of that range does.", number, - PqAnchorLapse.windowCovering(number).orElse(null)); + PqAnchorLapse.windowCovering(config.chainId(), number).orElse(null)); return false; } LOG.debug( "AERE PQ ANCHOR (R2): block {} accepted as written with {} seal(s); it falls inside the " + "historical range {} in which neither the threshold nor the ordering was in force.", number, certificate.size(), - PqAnchorLapse.windowCovering(number).orElse(null)); + PqAnchorLapse.windowCovering(config.chainId(), number).orElse(null)); return true; } catch (final Exception e) { LOG.warn( "AERE PQ ANCHOR (R2): block {} REJECTED - it falls inside a historical unenforced range " + "but its extraData could not be decoded: {}", number, e.toString()); return false; } } /** * AERE D-078: hand the parent's validator set to {@link FalconSealSupport} so its registry- * coverage report is about the height the chain is actually at. Swallows everything: a rule that * rejected a header because a diagnostic threw would be a worse defect than the one this repairs. * * @param parent the parent header * @param protocolContext the protocol context */ private void observeValidatorsForDiagnostics( diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java new file mode 100644 index 0000000000..d6b46c5396 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRule.java @@ -0,0 +1,133 @@ +/* + * 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.datatypes.Hash; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.mainnet.AttachedBlockHeaderValidationRule; + +import java.util.Collections; +import java.util.NavigableMap; +import java.util.TreeMap; + +import com.google.common.base.Splitter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AERE AIP-22 stage 4 (plan 9.6.7 point iv): the CHECKPOINT, which is how the weak subjectivity of a + * finality gadget is handled, stated publicly rather than hidden. A node that synchronises from + * genesis without other knowledge can be handed, by an eclipsing neighbour, a fork that ends before + * the first certified anchor; it can never reach the honest chain's height with valid certificates, + * but the eclipsed node does not know that height. Given a recent anchor from outside (height and + * block hash, from the published registry epoch or from its operator), it refuses any header at that + * height with another hash, so every chain it accepts runs through the checkpoint, and a rewrite at + * any height below it would have to reproduce every certificate above. + * + *

    {@code aere.pq.checkpoint=:[,:...]}. Unset or blank: + * inert (every header passes), which is every chain as it stands. A value it cannot parse refuses at + * load (AERE-PQC-CHECKPOINT-01), never silently degrades to "no checkpoint". ATTACHED, so it runs at + * import under SKIP_DETACHED, the mode full sync imports with (the same reason R1A exists). + */ +public class PqCheckpointRule implements AttachedBlockHeaderValidationRule { + + /** The system property that names the checkpoints. */ + public static final String PROPERTY = "aere.pq.checkpoint"; + + private static final Logger LOG = LoggerFactory.getLogger(PqCheckpointRule.class); + + private final NavigableMap checkpoints; + + /** + * A rule over the given checkpoints (height to block hash). + * + * @param checkpoints the checkpoints; empty means inert + */ + public PqCheckpointRule(final NavigableMap checkpoints) { + this.checkpoints = Collections.unmodifiableNavigableMap(new TreeMap<>(checkpoints)); + } + + /** + * The rule configured on this node by {@link #PROPERTY}. + * + * @return the rule + */ + public static PqCheckpointRule fromSystemConfiguration() { + return new PqCheckpointRule(parse(System.getProperty(PROPERTY))); + } + + /** + * The checkpoints named by this node's configuration. + * + * @return height to block hash, empty when unset + */ + public NavigableMap checkpoints() { + return checkpoints; + } + + /** + * Parses {@code :[,...]}; fail-closed on anything malformed. + * + * @param value the property value, possibly null + * @return height to block hash + */ + static NavigableMap parse(final String value) { + final TreeMap m = new TreeMap<>(); + if (value == null || value.isBlank()) { + return m; + } + for (final String part : Splitter.on(',').trimResults().omitEmptyStrings().split(value)) { + final int c = part.indexOf(':'); + if (c <= 0) { + throw new IllegalArgumentException( + "AERE-PQC-CHECKPOINT-01: " + PROPERTY + " entry '" + part + "' is not :"); + } + final long height; + final Hash hash; + try { + height = Long.parseLong(part.substring(0, c).trim()); + hash = Hash.fromHexString(part.substring(c + 1).trim()); + } catch (final RuntimeException e) { + throw new IllegalArgumentException( + "AERE-PQC-CHECKPOINT-01: " + PROPERTY + " entry '" + part + "' is not :<32-byte block hash>", e); + } + if (height < 1) { + throw new IllegalArgumentException( + "AERE-PQC-CHECKPOINT-01: " + PROPERTY + " height " + height + " must be at least 1"); + } + if (m.put(height, hash) != null) { + throw new IllegalArgumentException( + "AERE-PQC-CHECKPOINT-01: " + PROPERTY + " names height " + height + " twice"); + } + } + return m; + } + + @Override + public boolean validate( + final BlockHeader header, final BlockHeader parent, final ProtocolContext protocolContext) { + final Hash want = checkpoints.get(header.getNumber()); + if (want == null || want.equals(header.getHash())) { + return true; + } + LOG.warn( + "AERE-PQC-CHECKPOINT-02: block {} has hash {} but this node's checkpoint at that height is {}; REFUSED (a chain that does not run through the checkpoint is never adopted)", + header.getNumber(), + header.getHash(), + want); + return false; + } +} diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java new file mode 100644 index 0000000000..e292937e50 --- /dev/null +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealRule.java @@ -0,0 +1,259 @@ +/* + * 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.ConsensusRoundIdentifier; +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.blockcreation.BftProposerSelector; +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.Collection; +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. + * + *

    AERE D-406 (2026-09-25, measured on testnet 28001): a block PREPARED in one round and not + * committed before its timer is RE-PROPOSED unchanged in a later round r by the proposer of round r + * (QBFT's locked value; only element 3 changes, the coinbase stays the builder's), and every node seals + * the header with the accepted proposal's seal, which is the round-r proposer's. Requiring the coinbase + * refused every such header on every node: block 3,314,873 stood 128 s (rounds 1 to 4 refused with + * "a seal cannot vouch for another proposer", round 5 was the builder's own turn again, N = 5); at + * N = 10 the same wait is ten rounds, and with the builder down after the prepare it does not end. So + * the seal's id must be the coinbase OR, at a round above zero, the proposer of the round in element 3 + * as upstream's proposer selection computes it from the parent's proposer (its coinbase) and the + * header's own validator list (the set after the parent, which BftValidatorsValidationRule enforces). + * Every header the old rule admitted is admitted unchanged; the new branch only runs where it refused. + */ +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 r = refusal(header, parent); + r.ifPresent(LOG::info); + return r.isEmpty(); + } + + /** + * Why a header at or above H_hdr is refused, if it is, judged WITHOUT its parent: only a seal of the + * coinbase's index can be admitted (a re-proposed header needs the parent, {@link #refusal(BlockHeader, + * BlockHeader)}). + * + * @param header the header + * @return the refusal, or empty + */ + public Optional refusal(final BlockHeader header) { + return refusal(header, null); + } + + /** + * AERE D-406: the proposer of the round in element 3, as upstream's selection computes it (changeEachBlock, + * as QBFT), from the parent's proposer and the header's validator list. Empty at round 0, without the + * parent, or when the list cannot seed a selection. + * + * @param h the height + * @param round the round in element 3 + * @param parent the parent header, or null + * @param validators the header's validator list + * @return the proposer of that round, or empty + */ + static Optional

    proposerOfRound( + final long h, final int round, final BlockHeader parent, final Collection
    validators) { + if (round <= 0 || round > MAX_ROUND || parent == null || validators == null || validators.isEmpty()) { + return Optional.empty(); + } + try { + return Optional.of( + BftProposerSelector.selectProposerForRound( + new ConsensusRoundIdentifier(h, round), + parent.getCoinbase(), + validators, + true)); + } catch (final RuntimeException e) { + return Optional.empty(); + } + } + + /** + * AERE D-406: the highest round element 3 may carry from H_hdr. No honest round gets near it (the QBFT round timer doubles each + * round), and it keeps the rotation arithmetic, which upstream does in int, in range. The second client applies the same bound. + */ + static final int MAX_ROUND = 1024; + + /** + * AERE D-406: whether {@code coinbase} is the proposer of some round 0..round at this height, i.e. could have BUILT the block + * (a block is built by the proposer of the round it is first proposed in, and a re-proposal keeps its coinbase). The rotation is + * periodic in n, so rounds 0..min(round, n - 1) cover the set. The second client has required exactly this since D-331. + */ + static boolean builtByAProposerUpTo( + final long h, final int round, final BlockHeader parent, final Collection
    validators, final Address coinbase) { + final int last = Math.min(round, validators.size() - 1); + for (int r = 0; r <= last; r++) { + try { + if (BftProposerSelector.selectProposerForRound(new ConsensusRoundIdentifier(h, r), parent.getCoinbase(), validators, true) + .equals(coinbase)) { + return true; + } + } catch (final RuntimeException e) { + return false; + } + } + return false; + } + + /** + * Why a header at or above H_hdr is refused, if it is. + * + * @param header the header + * @param parent its parent, or null (then only the coinbase's seal can be admitted) + * @return the refusal, or empty + */ + public Optional refusal(final BlockHeader header, final BlockHeader parent) { + 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"); + } + // AERE D-406: the round bound, and with the parent, the builder: the coinbase is the proposer of some round up to element 3 + if (ed.getRound() < 0 || ed.getRound() > MAX_ROUND) { + return Optional.of("AERE PQ HEADER: block " + h + " round " + ed.getRound() + " in element 3 is outside 0.." + MAX_ROUND); + } + if (parent != null + && !ed.getValidators().isEmpty() + && !builtByAProposerUpTo(h, ed.getRound(), parent, ed.getValidators(), header.getCoinbase())) { + return Optional.of( + "AERE PQ HEADER: block " + h + " coinbase " + header.getCoinbase() + " is not the proposer of any round 0.." + + ed.getRound() + " at this height, so it did not build the block"); + } + final FalconSeal ps = ed.getPqProposerSeal().get(); + final Optional
    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())) { + // AERE D-406: a re-proposed block carries the seal of the proposer of the round it was committed in + final Optional
    roundProposer = proposerOfRound(h, ed.getRound(), parent, ed.getValidators()); + if (roundProposer.isEmpty() || !roundProposer.get().equals(id.get())) { + return Optional.of( + "AERE PQ HEADER: block " + h + " proposer seal of index " + ps.getValidatorIndex() + " (id " + id.get() + + ") but coinbase " + header.getCoinbase() + + (ed.getRound() > 0 + ? roundProposer + .map(p -> " and the proposer of round " + ed.getRound() + " is " + p) + .orElse(" and the proposer of round " + ed.getRound() + " is not known without the parent") + : "") + + " - 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(); + } +} diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java new file mode 100644 index 0000000000..e51e617a97 --- /dev/null +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/AereIdentityHeaderTest.java @@ -0,0 +1,144 @@ +/* + * AERE AIP-22 stage 3: the identity switch on the IMPORT path - the committed seals of a real header. + * + * Four node keys seal a QBFT header (BftExtraDataFixture signs each committed seal over the + * committed-seal hash of that very header, as a live committer does). The committers recovered from + * it, through BOTH places upstream recovers them (BftBlockInterface.getCommitters for the header + * rules, BftBlockHashing.recoverCommitterAddresses for the rest), must be the v3 ids from H_id, and + * the classical addresses one block below H_id or with the switch disarmed. That is exactly what lets + * BftCommitSealsValidationRule accept a header sealed by ids against a validator set of ids. + */ +package org.hyperledger.besu.consensus.qbft; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.consensus.common.bft.AerePqIdentity; +import org.hyperledger.besu.consensus.common.bft.BftBlockHashing; +import org.hyperledger.besu.consensus.common.bft.BftBlockHeaderFunctions; +import org.hyperledger.besu.consensus.common.bft.BftBlockInterface; +import org.hyperledger.besu.consensus.common.bft.BftExtraData; +import org.hyperledger.besu.consensus.common.bft.BftExtraDataCodec; +import org.hyperledger.besu.consensus.common.bft.BftExtraDataFixture; +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.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 java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.TreeSet; + +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 AereIdentityHeaderTest { + + private static final long CHAIN = 28001; + private static final long H_ID = 20; + private final QbftExtraDataCodec codec = new QbftExtraDataCodec(); + private final List keys = new ArrayList<>(); + private final List
    addr = new ArrayList<>(); + private final List
    ids = new ArrayList<>(); + + @BeforeEach + public void registry() { + final long bind = 10; + final List rows = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + final NodeKey k = NodeKeyUtils.generate(); + keys.add(k); + addr.add(Util.publicKeyToAddress(k.getPublicKey())); + final SealScheme f = SealSchemes.FALCON_512; + final SealScheme.GeneratedPair p = f.generate(SecureRandomProvider.createSecureRandom()); + final byte[] std = new byte[897]; + std[0] = 0x09; + System.arraycopy(p.publicRegistryForm(), 0, std, 1, 896); + final byte[] id = PqRegistryV3.idOf(std); + final byte[] pop = + f.sign( + p.privateKey(), + PqRegistryV3.popMessage(CHAIN, bind, 4, i, id, f.wireId(), std).toArrayUnsafe()) + .orElseThrow(); + final TreeMap km = new TreeMap<>(); + km.put(f.wireId() & 0xff, new PqRegistryV3.Key(f, std, pop)); + final Bytes32 rh = + PqRegistryV3.rowHash( + CHAIN, bind, 4, new PqRegistryV3.Row(i, id, km, null, Map.of(), null, null)); + final SECPSignature claim = k.sign(PqRegistryV3.claimDigest(CHAIN, rh)); + rows.add( + new PqRegistryV3.Row( + i, id, km, null, Map.of(), addr.get(i).getBytes().toArrayUnsafe(), + claim.encodedBytes().toArrayUnsafe())); + ids.add(Address.wrap(Bytes.wrap(id))); + } + final List hashes = new ArrayList<>(); + for (final PqRegistryV3.Row r : rows) { + hashes.add(PqRegistryV3.rowHash(CHAIN, bind, 4, r)); + } + final Bytes32 root = PqRegistryV3.merkleRoot(hashes); + final TreeMap epochs = new TreeMap<>(); + epochs.put( + bind, + new PqRegistryV3.Registry( + CHAIN, bind, 4, rows, hashes, root, PqRegistryV3.registryHash(CHAIN, bind, 4, root))); + AerePqIdentity.install(new AerePqIdentity.Config(H_ID, epochs)); + } + + @AfterEach + public void disarm() { + AerePqIdentity.uninstall(); + } + + /** A header at this height, listing these validators, sealed by all four keys. */ + private BlockHeader sealedHeader(final long number, final List
    validators) { + final BlockHeaderTestFixture b = new BlockHeaderTestFixture(); + b.number(number); + b.gasLimit(5000); + b.timestamp(6 * number); + b.coinbase(validators.get(0)); + b.blockHeaderFunctions(BftBlockHeaderFunctions.forCommittedSeal(codec)); + final BftExtraData ed = + BftExtraDataFixture.createExtraData( + b.buildHeader(), + Bytes.wrap(new byte[BftExtraDataCodec.EXTRA_VANITY_LENGTH]), + Optional.empty(), + new ArrayList<>(new TreeSet<>(validators)), + keys, + 0, + codec); + b.extraData(codec.encode(ed)); + return b.buildHeader(); + } + + @Test + public void fromHidTheCommittersOfARealHeaderAreTheIds() { + final BlockHeader h = sealedHeader(H_ID, ids); + assertThat(new BftBlockInterface(codec).getCommitters(h)).containsExactlyElementsOf(ids); + assertThat(new BftBlockHashing(codec).recoverCommitterAddresses(h, codec.decode(h))) + .containsExactlyElementsOf(ids); + } + + @Test + public void belowHidAndDisarmedTheyAreTheAddresses() { + final BlockHeader below = sealedHeader(H_ID - 1, addr); + assertThat(new BftBlockInterface(codec).getCommitters(below)).containsExactlyElementsOf(addr); + final BlockHeader at = sealedHeader(H_ID, ids); + AerePqIdentity.uninstall(); + assertThat(new BftBlockInterface(codec).getCommitters(at)).containsExactlyElementsOf(addr); + assertThat(new BftBlockHashing(codec).recoverCommitterAddresses(at, codec.decode(at))) + .containsExactlyElementsOf(addr); + } +} diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDisarmedWindowTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDisarmedWindowTest.java old mode 100755 new mode 100644 index 9451ec7b7f..e7604a4fca --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDisarmedWindowTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorDisarmedWindowTest.java @@ -122,46 +122,46 @@ public class PqAnchorDisarmedWindowTest { private final LocalRegistry registry = new LocalRegistry(); private final PqAnchorDigestRule r1 = new PqAnchorDigestRule(live); private final PqAnchorSealsRule r2 = new PqAnchorSealsRule(live, registry); // ----------------------------------------------------------------------------------------------- // The heights under test really are anchor heights. Without this the file measures nothing. // ----------------------------------------------------------------------------------------------- @Test public void everyHeightUnderTestIsAnAnchorHeightOfTheLiveConfiguration() { for (final long height : List.of(BEFORE, OFF_FIRST, OFF_MIDDLE, OFF_LAST, LOW_FIRST, LOW_MIDDLE, LOW_LAST, AFTER)) { assertThat(live.anchorAppliesAt(height)) .describedAs("height %d must be one the rules judge, or this file proves nothing", height) .isTrue(); assertThat(live.minSealsAt(height)) .describedAs("the schedule asks for three seals at height %d", height) .isEqualTo(3); } - assertThat(PqAnchorLapse.isDisarmed(OFF_FIRST)).isTrue(); - assertThat(PqAnchorLapse.isDisarmed(OFF_LAST)).isTrue(); - assertThat(PqAnchorLapse.isDisarmed(BEFORE)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(LOW_FIRST)).isFalse(); - assertThat(PqAnchorLapse.historicMinSeals(LOW_FIRST)).hasValue(1); - assertThat(PqAnchorLapse.historicMinSeals(AFTER)).isEmpty(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, OFF_FIRST)).isTrue(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, OFF_LAST)).isTrue(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, BEFORE)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, LOW_FIRST)).isFalse(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOW_FIRST)).hasValue(1); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, AFTER)).isEmpty(); } // ----------------------------------------------------------------------------------------------- // RANGE 1, nothing in force: a header of the shape the chain holds is accepted by both rules. // ----------------------------------------------------------------------------------------------- @Test public void r1AcceptsAHeaderOfTheFirstRangeAsWritten() { for (final long height : List.of(OFF_FIRST, OFF_MIDDLE, OFF_LAST)) { final BlockHeader parent = parentHeader(height - 1L); final BlockHeader block = unenforcedHeader(height, parent, ARRIVAL_ORDER); assertThat(r1.validate(block, parent)) .describedAs( "height %d carries the client version string in vanityData and a certificate in " + "arrival order; that is what the chain holds, and a node syncing from genesis " + "must be able to pass it", height) .isTrue(); } } @@ -366,43 +366,43 @@ public class PqAnchorDisarmedWindowTest { assertThat(r1.validate(unenforcedHeader(LOW_FIRST, lowParent, ARRIVAL_ORDER), lowParent)) .isFalse(); // And the far end of the lowered range, to one anchor step, on the threshold. final BlockHeader lowLastParent = parentHeader(LOW_LAST - 1L); final ProtocolContext context = contextWith(VALIDATORS); assertThat(r2.validate(honestBlock(LOW_LAST, lowLastParent, List.of(0)), lowLastParent, context)) .isTrue(); final BlockHeader afterParent = parentHeader(AFTER - 1L); assertThat(r2.validate(honestBlock(AFTER, afterParent, List.of(0)), afterParent, context)) .isFalse(); } @Test public void theBoundsAreExactToOneBlockOnBothRules() { // The heights either side of the bounds are not anchor heights, so both rules stand down there // for the ordinary reason. Asserted so that "accepted" at OFF_FIRST - 1 is not mistaken for the // exception having leaked one block down. assertThat(live.anchorAppliesAt(OFF_FIRST - 1L)).isFalse(); assertThat(live.anchorAppliesAt(OFF_LAST + 1L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(OFF_FIRST - 1L)).isFalse(); - assertThat(PqAnchorLapse.isDisarmed(OFF_LAST + 1L)).isFalse(); - assertThat(PqAnchorLapse.historicMinSeals(LOW_LAST + 1L)).isEmpty(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, OFF_FIRST - 1L)).isFalse(); + assertThat(PqAnchorLapse.isDisarmed(PqAnchorLapse.CHAIN_ID, OFF_LAST + 1L)).isFalse(); + assertThat(PqAnchorLapse.historicMinSeals(PqAnchorLapse.CHAIN_ID, LOW_LAST + 1L)).isEmpty(); } // ----------------------------------------------------------------------------------------------- // What the exception deliberately does NOT relax, at any height. // ----------------------------------------------------------------------------------------------- @Test public void insideTheFirstRangeARepeatedIndexIsStillRefusedByBothRules() { // Sortedness fixes one accepted order for a given set; distinctness is what makes "repeat one // seal to inflate the count" unrepresentable. Measured on every header of the range: distinct // in all of them. So this refusal costs nothing and keeps the attack unrepresentable. final BlockHeader parent = parentHeader(OFF_FIRST - 1L); final BlockHeader repeated = unenforcedHeader(OFF_FIRST, parent, List.of(2, 6, 2)); assertThat(r1.validate(repeated, parent)).isFalse(); assertThat(r2.validate(repeated, parent, contextWith(VALIDATORS))).isFalse(); // Positive control: the same rule at the same height accepts the distinct version. final BlockHeader distinct = unenforcedHeader(OFF_FIRST, parent, ARRIVAL_ORDER); assertThat(r1.validate(distinct, parent)).isTrue(); assertThat(r2.validate(distinct, parent, contextWith(VALIDATORS))).isTrue(); @@ -411,72 +411,146 @@ public class PqAnchorDisarmedWindowTest { @Test public void insideTheFirstRangeAnUndecodableHeaderIsStillRefused() { final BlockHeader parent = parentHeader(OFF_FIRST - 1L); final BlockHeader garbage = header(OFF_FIRST, parent.getHash(), Bytes.fromHexString("0xdeadbeef")); assertThat(r1.validate(garbage, parent)).isFalse(); assertThat(r2.validate(garbage, parent, contextWith(VALIDATORS))).isFalse(); } @Test public void belowTheActivationHeightNothingIsJudgedAtAll() { // The ranges sit far above H, so this only restates the height gate; it is here because a // future edit that moved the range gates ABOVE the height gate would make the rules decode // headers on the whole existing chain, which is the property that lets this binary be warmed. final BlockHeader parent = parentHeader(H_LIVE - 2L); final BlockHeader block = unenforcedHeader(H_LIVE - 1L, parent, ARRIVAL_ORDER); assertThat(r1.validate(block, parent)).isTrue(); assertThat(r2.validate(block, parent, contextWith(VALIDATORS))).isTrue(); } + // ----------------------------------------------------------------------------------------------- + // D-428 (2026-09-28): the ranges are facts about chain 2800. The same heights on ANY other chain are + // judged by the plain rules, as the second client does. Each case carries its positive control, so a + // refusal here cannot come from a signature made for the wrong chain. + // ----------------------------------------------------------------------------------------------- + + private static final long OTHER_CHAIN = 28001L; + + private final PqAnchorConfig otherChain = + new PqAnchorConfig( + OTHER_CHAIN, + H_LIVE, + Map.of(H_LIVE, 0, 13_034_000L, 3), + OptionalInt.empty(), + false, + OptionalInt.empty(), + OptionalInt.of(INTERVAL)); + + private final PqAnchorDigestRule r1Other = new PqAnchorDigestRule(otherChain); + private final PqAnchorSealsRule r2Other = new PqAnchorSealsRule(otherChain, registry); + + @Test + public void onAnotherChainTheFirstRangeIsJudgedByThePlainRules() { + for (final long height : List.of(OFF_FIRST, OFF_MIDDLE, OFF_LAST)) { + final BlockHeader parent = parentHeader(height - 1L); + final ProtocolContext context = contextWith(VALIDATORS); + final BlockHeader unenforced = unenforcedHeader(height, parent, ARRIVAL_ORDER); + assertThat(r1.validate(unenforced, parent)) + .describedAs("positive control: on chain 2800 height %d is inside the first range", height) + .isTrue(); + assertThat(r1Other.validate(unenforced, parent)) + .describedAs("on chain %d there was no lapse: no digest must be refused at %d", OTHER_CHAIN, height) + .isFalse(); + assertThat(r2Other.validate(unenforced, parent, context)) + .describedAs("on chain %d the arrival order and the missing digest are refused at %d", OTHER_CHAIN, height) + .isFalse(); + final BlockHeader honest = honestBlockOn(OTHER_CHAIN, height, parent, List.of(0, 1, 2)); + assertThat(r1Other.validate(honest, parent)) + .describedAs("positive control: an honest header OF chain %d is accepted at %d", OTHER_CHAIN, height) + .isTrue(); + assertThat(r2Other.validate(honest, parent, context)).isTrue(); + } + } + + @Test + public void onAnotherChainTheSecondRangeKeepsTheScheduledThreshold() { + for (final long height : List.of(LOW_FIRST, LOW_MIDDLE, LOW_LAST)) { + final BlockHeader parent = parentHeader(height - 1L); + final ProtocolContext context = contextWith(VALIDATORS); + assertThat(r2.validate(honestBlock(height, parent, List.of(5)), parent, context)) + .describedAs("positive control: on chain 2800 one seal passes at %d, the threshold in force then", height) + .isTrue(); + assertThat(r2Other.validate(honestBlockOn(OTHER_CHAIN, height, parent, List.of(5)), parent, context)) + .describedAs("on chain %d the threshold at %d is the scheduled three, not the 2800 lapse", OTHER_CHAIN, height) + .isFalse(); + assertThat(r2Other.validate(honestBlockOn(OTHER_CHAIN, height, parent, List.of(0, 1, 5)), parent, context)) + .describedAs("positive control: three seals OF chain %d pass at %d", OTHER_CHAIN, height) + .isTrue(); + } + } + // ----------------------------------------------------------------------------------------------- // Helpers. // ----------------------------------------------------------------------------------------------- /** * A header of the shape the first range holds: the client version string in vanityData instead of * a digest, and the certificate written in the order the commits arrived. */ private BlockHeader unenforcedHeader( final long number, final BlockHeader parent, final List indicesInArrivalOrder) { return header( number, parent.getHash(), extraData(CLIENT_VANITY, sealsFor(parent, indicesInArrivalOrder))); } /** A header whose digest is correctly bound but whose indices are left in arrival order. */ private BlockHeader digestBoundButUnsorted( final long number, final BlockHeader parent, final List indicesInArrivalOrder) { final List seals = sealsFor(parent, indicesInArrivalOrder); final Bytes32 digest = PqAnchor.anchorDigest(CHAIN_ID, parent.getNumber(), parent.getHash().getBytes(), seals); return header(number, parent.getHash(), extraData(digest, seals)); } /** A header an honest proposer under the armed rules writes: digest bound, indices sorted. */ private BlockHeader honestBlock( final long number, final BlockHeader parent, final List indices) { final List sorted = PqAnchor.sortedByIndex(sealsFor(parent, indices)); final Bytes32 digest = PqAnchor.anchorDigest(CHAIN_ID, parent.getNumber(), parent.getHash().getBytes(), sorted); return header(number, parent.getHash(), extraData(digest, sorted)); } + /** An honest header for ANY chain: the digest and the seals are computed for that chain (D-428). */ + private BlockHeader honestBlockOn( + final long chain, final long number, final BlockHeader parent, final List indices) { + final Bytes32 message = PqAnchor.commitMessage(chain, parent.getNumber(), parent.getHash().getBytes()); + final List seals = new ArrayList<>(); + for (final int index : indices) { + seals.add(new FalconSeal(index, LocalRegistry.sign(index, message))); + } + final List sorted = PqAnchor.sortedByIndex(seals); + final Bytes32 digest = PqAnchor.anchorDigest(chain, parent.getNumber(), parent.getHash().getBytes(), sorted); + return header(number, parent.getHash(), extraData(digest, sorted)); + } + private List sealsFor(final BlockHeader parent, final List indices) { // One return, one mutability. An early return of an immutable empty list here is what // errorprone's MixedMutabilityReturnType refuses, and it is refusing something real: the empty // case is not special, it is the case where the loop runs zero times. final Bytes32 message = PqAnchor.commitMessage(CHAIN_ID, parent.getNumber(), parent.getHash().getBytes()); final List seals = new ArrayList<>(); for (final int index : indices) { seals.add(new FalconSeal(index, LocalRegistry.sign(index, message))); } return seals; } private ProtocolContext contextWith(final Collection
    parentValidators) { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class, withSettings().strictness(Strictness.LENIENT)); when(validatorProvider.getValidatorsForBlock(any())).thenReturn(parentValidators); final BftContext bftContext = mock(BftContext.class, withSettings().strictness(Strictness.LENIENT)); when(bftContext.getValidatorProvider()).thenReturn(validatorProvider); diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorV2RulesTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorV2RulesTest.java index 4c542fe876..867b46b6f6 100755 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorV2RulesTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqAnchorV2RulesTest.java @@ -180,40 +180,74 @@ public class PqAnchorV2RulesTest { @Test public void r1RefusesAV2CertificateBelowTheV2HeightAndAV1CertificateAtIt() { // v2 below V2: the anchor height V2-1 (interval absent => every height from H is an anchor height) final BlockHeader parentBelow = parentHeader(V2 - 2L); final BlockHeader v2Early = honestV2(V2 - 1L, parentBelow, certificate(parentBelow, 5, 5)); assertThat(r1.validate(v2Early, parentBelow)).isFalse(); // v1 at V2: an honest v1 header (correct v1 digest) is still refused, the form is wrong final BlockHeader parent = parentHeader(V2 + 4L); final BlockHeader v1Late = PqAnchorTestSupport.honestHeader(V2 + 5L, parent.getHash(), List.of(PqAnchorTestSupport.seal(0), PqAnchorTestSupport.seal(1), PqAnchorTestSupport.seal(2))); assertThat(r1.validate(v1Late, parent)).isFalse(); // control: the same v1 header is accepted by a rule whose V2 lies in the future final PqAnchorConfig v1World = new PqAnchorConfig( CHAIN_ID, H, Map.of(H, 0, V2, K), OptionalInt.empty(), false, OptionalInt.empty(), OptionalInt.empty(), V2 + 1_000L); assertThat(new PqAnchorDigestRule(v1World).validate(v1Late, parent)).isTrue(); } // --------------------------------------------------------------------------------------- R2 + /** + * AIP-22 stage 2, per-scheme intervals: Falcon on the 32 grid and SLH-DSA on the 128 grid, both + * counted from H. At an anchor on the 128 grid the certificate must reach K SLH-DSA seals; at an + * anchor on the 32 grid only, K Falcon seals suffice, and SLH-DSA seals carried there anyway are a + * surplus that is VERIFIED (a tampered one still refuses), not ignored and not refused. + */ + @Test + public void r2WithPerSchemeIntervalsDemandsSlhDsaOnlyOnItsGridAndVerifiesSurplus() throws Exception { + armHybrid("0:falcon-512/32+" + SLH + "/128"); + long hybridAnchor = H; + while (hybridAnchor <= V2) { + hybridAnchor += 128L; + } + final long falconOnlyAnchor = hybridAnchor + 32L; + final BlockHeader p1 = parentHeader(hybridAnchor - 1L); + assertThat(r2.validate(honestV2(hybridAnchor, p1, certificate(p1, K, 0)), p1, context())).isFalse(); + assertThat(r2.validate(honestV2(hybridAnchor, p1, certificate(p1, K, K)), p1, context())).isTrue(); + final BlockHeader p2 = parentHeader(falconOnlyAnchor - 1L); + assertThat(r2.validate(honestV2(falconOnlyAnchor, p2, certificate(p2, K, 0)), p2, context())).isTrue(); + assertThat(r2.validate(honestV2(falconOnlyAnchor, p2, certificate(p2, K, K)), p2, context())).isTrue(); + final List surplus = new ArrayList<>(certificate(p2, K, K)); + for (int i = 0; i < surplus.size(); i++) { + final SchemeSeal s = surplus.get(i); + if (s.getSchemeWireId() == SealSchemes.SLH_DSA_128S.wireId()) { + final byte[] b = s.getSignature().toArray(); + b[b.length / 2] ^= 1; + surplus.set(i, new SchemeSeal(s.getSchemeWireId(), s.getValidatorIndex(), Bytes.wrap(b))); + break; + } + } + surplus.sort(PqAnchorV2.CANONICAL); + assertThat(r2.validate(honestV2(falconOnlyAnchor, p2, surplus), p2, context())).isFalse(); + } + @Test public void r2AcceptsTheHonestV2HeaderWithKSealsPerScheme() { final BlockHeader parent = parentHeader(V2 + 4L); assertThat(r2.validate(honestV2(V2 + 5L, parent, certificate(parent, K, K)), parent, context())).isTrue(); // more than K of each is fine too: K is a floor assertThat(r2.validate(honestV2(V2 + 5L, parent, certificate(parent, 5, 5)), parent, context())).isTrue(); } @Test public void r2RefusesAnSlhDsaSealFromAnIndexWithoutAFalconSeal() { final BlockHeader parent = parentHeader(V2 + 4L); final List tagged = certificate(parent, K, K + 1); // SLH-DSA from index K has no Falcon assertThat(r2.validate(honestV2(V2 + 5L, parent, tagged), parent, context())).isFalse(); } @Test public void r2RefusesACertificateShortOfOneScheme() { final BlockHeader parent = parentHeader(V2 + 4L); assertThat(r2.validate(honestV2(V2 + 5L, parent, certificate(parent, 5, K - 1)), parent, context())) .describedAs("K Falcon seals and only K-1 SLH-DSA: the schedule demands K of EVERY scheme") diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java new file mode 100644 index 0000000000..dfdc78622c --- /dev/null +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqCheckpointRuleTest.java @@ -0,0 +1,75 @@ +/* + * 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.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; + +import java.util.TreeMap; + +import org.junit.jupiter.api.Test; + +/** + * AIP-22 stage 4: the checkpoint rule. At the checkpoint height only the checkpoint's hash passes; + * every other height passes; unset is inert; a malformed value refuses at load with its reason. The + * negative control of the runner plants "never refuse" at run time and turns the refusal probe red. + */ +class PqCheckpointRuleTest { + + private static BlockHeader header(final long number, final long timestamp) { + return new BlockHeaderTestFixture().number(number).timestamp(timestamp).buildHeader(); + } + + @Test + void atTheCheckpointHeightOnlyTheCheckpointHashPassesAndAnotherHashIsRefused() { + final BlockHeader honest = header(2048, 1000); + final BlockHeader fork = header(2048, 1001); + assertThat(fork.getHash()).isNotEqualTo(honest.getHash()); + final TreeMap cp = new TreeMap<>(); + cp.put(2048L, honest.getHash()); + final PqCheckpointRule rule = new PqCheckpointRule(cp); + assertThat(rule.validate(honest, null, null)).isTrue(); + assertThat(rule.validate(fork, null, null)).isFalse(); + // other heights are not judged by the checkpoint + assertThat(rule.validate(header(2047, 999), null, null)).isTrue(); + assertThat(rule.validate(header(2049, 1002), null, null)).isTrue(); + } + + @Test + void unsetIsInertAndTheParserReadsSeveralCheckpoints() { + final PqCheckpointRule inert = new PqCheckpointRule(PqCheckpointRule.parse(null)); + assertThat(inert.checkpoints()).isEmpty(); + assertThat(inert.validate(header(2048, 1001), null, null)).isTrue(); + assertThat(PqCheckpointRule.parse(" ")).isEmpty(); + final String h1 = header(10, 1).getHash().toHexString(); + final String h2 = header(20, 2).getHash().toHexString(); + assertThat(PqCheckpointRule.parse("10:" + h1 + ", 20:" + h2)).hasSize(2).containsKeys(10L, 20L); + } + + @Test + void aMalformedValueRefusesAtLoadWithItsReason() { + final String h = header(10, 1).getHash().toHexString(); + for (final String bad : new String[] {"10", "x:" + h, "10:0x1234", "0:" + h, "10:" + h + ",10:" + h}) { + assertThatThrownBy(() -> PqCheckpointRule.parse(bad)) + .as(bad) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("AERE-PQC-CHECKPOINT-01"); + } + } +} diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java new file mode 100644 index 0000000000..2a928bda7c --- /dev/null +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/PqHeaderSealStage5Test.java @@ -0,0 +1,478 @@ +/* + * 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 org.hyperledger.besu.ethereum.rlp.RLPException; + +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 falcon = new ArrayList<>(); + private final List
    ids = new ArrayList<>(); + private PqHeaderSealRule rule; + + @BeforeEach + public void arm() { + final List 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 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 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 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 seals, final Optional 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 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(); + } + + // ---- AERE D-406 (2026-09-25): a block re-proposed at round r carries the seal of round r's proposer ---- + + /** The validator list as a real header stores it: ascending. */ + private List
    sortedIds() { + final List
    s = new ArrayList<>(ids); + Collections.sort(s); + return s; + } + + /** + * The proposer of round r written HERE, independently of the rule (which calls upstream's selector): the + * existing-proposer case is list[(indexOf(prev) + 1 + r) mod n], the missing-proposer case seeds from the first + * validator strictly above prev (the first if none) with no +1. + */ + private static Address expectedProposer(final List
    sorted, final Address prev, final int r) { + final int n = sorted.size(); + final int i = sorted.indexOf(prev); + if (i >= 0) { + return sorted.get((i + 1 + r) % n); + } + int seed = 0; + for (int k = 0; k < n; k++) { + if (sorted.get(k).compareTo(prev) > 0) { + seed = k; + break; + } + } + return sorted.get((seed + r) % n); + } + + private BlockHeader parentWithCoinbase(final long h, final Address coinbase) { + return new BlockHeaderTestFixture().number(h - 1).coinbase(coinbase).buildHeader(); + } + + /** A header at h, round r, built by {@code coinbase}, sealed as index {@code index} by key {@code signer}. */ + private BlockHeader reproposed(final long h, final int round, final Address coinbase, final int index, final int signer) { + final List
    sorted = sortedIds(); + final BftExtraData bare = + new BftExtraData( + Bytes.wrap(new byte[32]), List.of(), Optional.empty(), round, sorted, Collections.emptyList(), + Collections.emptyList(), Optional.empty()); + final BlockHeader unsealed = header(h, coinbase, bare); + final BftExtraData sealed = + new BftExtraData( + Bytes.wrap(new byte[32]), List.of(), Optional.empty(), round, sorted, Collections.emptyList(), + Collections.emptyList(), sealOver(unsealed, round, index, signer)); + return header(h, coinbase, sealed); + } + + @Test + public void aReProposedHeaderSealedByTheProposerOfItsRoundIsAdmittedOnlyWithItsParent() { + final List
    sorted = sortedIds(); + final long h = H_HDR + 5; + int judged = 0; + for (int p = 0; p < N; p++) { + final Address prev = sorted.get(p); + final Address builder = expectedProposer(sorted, prev, 0); + for (int r = 1; r < 2 * N; r++) { + final Address roundProposer = expectedProposer(sorted, prev, r); + if (roundProposer.equals(builder)) { + continue; + } + final int idx = ids.indexOf(roundProposer); + final BlockHeader hd = reproposed(h, r, builder, idx, idx); + final BlockHeader parent = parentWithCoinbase(h, prev); + assertThat(rule.refusal(hd, parent)).as("prev %s round %s", p, r).isEmpty(); + assertThat(rule.validate(hd, parent, null)).as("prev %s round %s", p, r).isTrue(); + // without the parent the round's proposer is not known: refused, and the reason says why + assertThat(rule.refusal(hd)).get().asString() + .contains("cannot vouch").contains("not known without the parent"); + judged++; + } + } + // every (prev, round) pair whose round proposer differs from the builder was judged: 4 x (7 - 1) at N = 4 + assertThat(judged).isEqualTo(N * (2 * N - 1 - 1)); + } + + @Test + public void aReProposedHeaderSealedByAThirdValidatorIsRefusedAndTheReasonNamesTheRoundsProposer() { + final List
    sorted = sortedIds(); + final long h = H_HDR + 6; + final Address prev = sorted.get(0); + final Address builder = expectedProposer(sorted, prev, 0); + final int r = 1; + final Address roundProposer = expectedProposer(sorted, prev, r); + Address third = null; + for (final Address a : sorted) { + if (!a.equals(builder) && !a.equals(roundProposer)) { + third = a; + break; + } + } + final int idx = ids.indexOf(third); + final Optional why = rule.refusal(reproposed(h, r, builder, idx, idx), parentWithCoinbase(h, prev)); + assertThat(why).get().asString() + .contains("cannot vouch") + .contains("the proposer of round " + r + " is " + roundProposer); + // the round proposer's key over ANOTHER round than element 3: the id passes, the signature does not + final int rp = ids.indexOf(roundProposer); + final List
    s2 = sortedIds(); + final BlockHeader atRound0 = + header(h, builder, new BftExtraData(Bytes.wrap(new byte[32]), List.of(), Optional.empty(), 0, s2, + Collections.emptyList(), Collections.emptyList(), Optional.empty())); + final BlockHeader wrongRound = + header(h, builder, new BftExtraData(Bytes.wrap(new byte[32]), List.of(), Optional.empty(), r, s2, + Collections.emptyList(), Collections.emptyList(), sealOver(atRound0, 0, rp, rp))); + assertThat(rule.refusal(wrongRound, parentWithCoinbase(h, prev))).get().asString().contains("does not verify"); + } + + @Test + public void theSealMustBeExactlyTheProposerOfRoundRNotOfAnEarlierRound() { + // found by the adversarial review of 2026-09-25: an implementation admitting ANY P(r') with r' <= r passed every probe above + final List
    sorted = sortedIds(); + final long h = H_HDR + 9; + final Address prev = sorted.get(0); + final Address builder = expectedProposer(sorted, prev, 0); + final Address p1 = expectedProposer(sorted, prev, 1); + final Address p2 = expectedProposer(sorted, prev, 2); + assertThat(p1).isNotEqualTo(p2); + final int i1 = ids.indexOf(p1); + assertThat(rule.refusal(reproposed(h, 2, builder, i1, i1), parentWithCoinbase(h, prev))).get().asString() + .contains("cannot vouch").contains("the proposer of round 2 is " + p2); + final int i2 = ids.indexOf(p2); + assertThat(rule.refusal(reproposed(h, 2, builder, i2, i2), parentWithCoinbase(h, prev))).isEmpty(); + } + + @Test + public void withTheParentTheCoinbaseMustBeTheProposerOfSomeRoundUpToElement3() { + // the second client has refused this since D-331; the Besu-derived rule did not, so the two judged such a header differently + final List
    sorted = sortedIds(); + final long h = H_HDR + 10; + final Address prev = sorted.get(0); + final Address p1 = expectedProposer(sorted, prev, 1); + final Address p3 = expectedProposer(sorted, prev, 3); + final int i1 = ids.indexOf(p1); + // round 1, built by P(3) (a proposer of a LATER round), sealed by P(1): the seal is the round's, the builder is impossible + assertThat(rule.refusal(reproposed(h, 1, p3, i1, i1), parentWithCoinbase(h, prev))).get().asString() + .contains("is not the proposer of any round 0..1"); + // and a coinbase-sealed header at round 0 whose coinbase is not P(0) + final int i3 = ids.indexOf(p3); + assertThat(rule.refusal(reproposed(h, 0, p3, i3, i3), parentWithCoinbase(h, prev))).get().asString() + .contains("is not the proposer of any round 0..0"); + // without the parent the builder cannot be judged, and the coinbase's own seal is admitted as before + assertThat(rule.refusal(reproposed(h, 0, p3, i3, i3))).isEmpty(); + } + + @Test + public void theRoundInElement3IsBoundedAndTheBoundIsInclusive() { + final List
    sorted = sortedIds(); + final long h = H_HDR + 11; + final Address prev = sorted.get(2); + final int max = PqHeaderSealRule.MAX_ROUND; + // at the bound: a re-proposal sealed by P(max) is admitted (upstream's int arithmetic stays in range) + final Address builder = expectedProposer(sorted, prev, 0); + final Address pm = expectedProposer(sorted, prev, max); + final int im = ids.indexOf(pm); + final int ib = ids.indexOf(builder); + final Address sealer = pm.equals(builder) ? builder : pm; + final int is = sealer.equals(builder) ? ib : im; + assertThat(rule.refusal(reproposed(h, max, builder, is, is), parentWithCoinbase(h, prev))).isEmpty(); + // above it: refused by the bound, with or without the parent, whoever sealed + assertThat(rule.refusal(reproposed(h, max + 1, builder, ib, ib), parentWithCoinbase(h, prev))).get().asString() + .contains("is outside 0.." + max); + assertThat(rule.refusal(reproposed(h, max + 1, builder, ib, ib))).get().asString().contains("is outside 0.." + max); + } + + @Test + public void atRoundZeroOnlyTheCoinbaseSealIsAdmittedEvenWithTheParent() { + final List
    sorted = sortedIds(); + final long h = H_HDR + 7; + final Address prev = sorted.get(1); + final Address builder = expectedProposer(sorted, prev, 0); + for (final Address other : sorted) { + if (other.equals(builder)) { + continue; + } + final int idx = ids.indexOf(other); + final Optional why = rule.refusal(reproposed(h, 0, builder, idx, idx), parentWithCoinbase(h, prev)); + assertThat(why).get().asString().contains("cannot vouch").doesNotContain("proposer of round"); + } + final int b = ids.indexOf(builder); + assertThat(rule.refusal(reproposed(h, 0, builder, b, b), parentWithCoinbase(h, prev))).isEmpty(); + } + + @Test + public void whenTheParentsProposerLeftTheSetTheRoundsProposerFollowsUpstreamsMissingProposerSeed() { + final List
    sorted = sortedIds(); + final long h = H_HDR + 8; + // a parent proposer below every id (the seed is the first validator) and one above every id (it wraps to the first) + final Address below = Address.fromHexString("0x0000000000000000000000000000000000000001"); + final Address above = Address.fromHexString("0xffffffffffffffffffffffffffffffffffffffff"); + // and one strictly between the second and the third id, so the seed is the third (index 2), not the first + final byte[] mid = sorted.get(1).getBytes().toArrayUnsafe().clone(); + int k = mid.length - 1; + while (k >= 0 && mid[k] == (byte) 0xff) { + mid[k--] = 0; + } + mid[k]++; + final Address middle = Address.wrap(Bytes.wrap(mid)); + assertThat(middle.compareTo(sorted.get(1)) > 0 && middle.compareTo(sorted.get(2)) < 0).isTrue(); + assertThat(expectedProposer(sorted, middle, 0)).isEqualTo(sorted.get(2)); + int judged = 0; + for (final Address prev : List.of(below, above, middle)) { + final Address builder = expectedProposer(sorted, prev, 0); + for (int r = 1; r < N; r++) { + final Address rp = expectedProposer(sorted, prev, r); + final int idx = ids.indexOf(rp); + assertThat(rule.refusal(reproposed(h, r, builder, idx, idx), parentWithCoinbase(h, prev))) + .as("prev %s round %s", prev, r).isEmpty(); + judged++; + } + } + assertThat(judged).isEqualTo(3 * (N - 1)); + } + + // ---- AERE D-415 (2026-09-25): the vanity field is exactly 32 bytes at EVERY height, in the codec, as in the second client ---- + + /** The genesis extraData of chains 2800 and 28001 (both a 32-zero vanity), as published. */ + private static final String GENESIS_2800_EXTRA = + "0xf865a00000000000000000000000000000000000000000000000000000000000000000f83f94b5e768cb3117ea25175f51aa956b48691025c2a894f73bbbd26a82dbc769066550d8d4585eab2fc00d944bf6f91d84b37454544279cba152581ed6820044c080c0"; + private static final String GENESIS_28001_EXTRA = + "0xf87aa00000000000000000000000000000000000000000000000000000000000000000f8549404211fd71c1eae94f560e7e173399c91c15fd9b394070d5e1534a367430307a6e56bef29ddff79c0e394b84ec13c5811264f60a8bc392634709558223ea694fb6be1504acf8262cf98669f7833b9cf1cbfabb9c080c0"; + /** A real 32-byte vanity between anchors on 28001 today: the client version string, not a digest. */ + private static final String VANITY_VERSIUNE = "0x626573752032362e342e302d616572652e312d643230333230312d7774396239"; + + private BlockHeader cuVanity(final long h, final Bytes vanity) { + final BftExtraData ed = + new BftExtraData(vanity, List.of(), Optional.empty(), 0, ids, Collections.emptyList(), Collections.emptyList(), Optional.empty()); + return new BlockHeaderTestFixture().number(h).coinbase(ids.get(0)).extraData(CODEC.encode(ed)).buildHeader(); + } + + @Test + public void theVanityFieldIsExactly32BytesAtEveryHeightAndBothGenesesStillDecode() { + // the two real geneses and a real between-anchors vanity decode as before (the check cannot touch the chain) + for (final String g : List.of(GENESIS_2800_EXTRA, GENESIS_28001_EXTRA)) { + assertThat(CODEC.decodeRaw(Bytes.fromHexString(g)).getVanityData().size()).as("genesis").isEqualTo(32); + } + assertThat(CODEC.decode(cuVanity(H_HDR - 5, Bytes.fromHexString(VANITY_VERSIUNE))).getVanityData().size()).isEqualTo(32); + // found by the differential fuzzer: a 0-, 31-, 33- or 64-byte vanity between anchors was admitted here and refused by the + // second client; now the codec refuses it at any height, above and below H_hdr, and the header rule reports the reason + for (final int n : new int[] {0, 31, 33, 64}) { + final Bytes v = Bytes.wrap(new byte[n]); + assertThatThrownBy(() -> CODEC.decode(cuVanity(H_HDR + 12, v))).as("vanity %s above H_hdr", n) + .isInstanceOf(RLPException.class).hasMessageContaining("AERE-D415"); + assertThatThrownBy(() -> CODEC.decode(cuVanity(H_HDR - 5, v))).as("vanity %s below H_hdr", n) + .isInstanceOf(RLPException.class).hasMessageContaining("AERE-D415"); + assertThat(rule.refusal(cuVanity(H_HDR + 12, v))).get().asString().contains("does not decode").contains("AERE-D415"); + } + } + + @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"); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java index cb1d879a05..5c74d44292 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetBalance.java @@ -1,70 +1,86 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import java.util.function.Supplier; public class EthGetBalance extends AbstractBlockParameterOrBlockHashMethod { public EthGetBalance(final BlockchainQueries blockchainQueries) { super(blockchainQueries); } public EthGetBalance(final Supplier blockchainQueries) { super(blockchainQueries); } @Override public String getName() { return RpcMethod.ETH_GET_BALANCE.getMethodName(); } @Override protected BlockParameterOrBlockHash blockParameterOrBlockHash( final JsonRpcRequestContext request) { try { return request.getRequiredParameter(1, BlockParameterOrBlockHash.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid block or block hash parameter (index 1)", RpcErrorType.INVALID_BLOCK_PARAMS, e); } } + // AERE D-FAZA2-04 (2026-09-10): an EMPTY answer from the state query means the world state for that + // block is not available (outside the node's state window, or not yet persisted); it never means + // "no such account" - fromAccount() fills a default for that. Upstream serialised the empty answer as + // a bare JSON null, which every client reads as a value. Here it is the same honest error that + // eth_getProof already returns: WORLD_STATE_UNAVAILABLE. @Override - protected String resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { + protected Object resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { final Address address; try { address = request.getRequiredParameter(0, Address.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid address parameter (index 0)", RpcErrorType.INVALID_ADDRESS_PARAMS, e); } return blockchainQueries .get() .accountBalance(address, blockHash) .map(Quantity::create) - .orElse(null); + .map(s -> s) + .orElseGet( + () -> + new JsonRpcErrorResponse( + request.getRequest().getId(), RpcErrorType.WORLD_STATE_UNAVAILABLE)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java index 0c9194b0b3..52bc65eece 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetCode.java @@ -1,68 +1,87 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; public class EthGetCode extends AbstractBlockParameterOrBlockHashMethod { public EthGetCode(final BlockchainQueries blockchainQueries) { super(blockchainQueries); } public EthGetCode(final Supplier blockchainQueries) { super(blockchainQueries); } @Override public String getName() { return RpcMethod.ETH_GET_CODE.getMethodName(); } @Override protected BlockParameterOrBlockHash blockParameterOrBlockHash( final JsonRpcRequestContext request) { try { return request.getRequiredParameter(1, BlockParameterOrBlockHash.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid block or block hash parameter (index 1)", RpcErrorType.INVALID_BLOCK_PARAMS, e); } } + // AERE D-FAZA2-04 (2026-09-10): an EMPTY answer from the state query means the world state for that + // block is not available (outside the node's state window, or not yet persisted); it never means + // "no such account" - fromAccount() fills a default for that. Upstream serialised the empty answer as + // a bare JSON null, which every client reads as a value. Here it is the same honest error that + // eth_getProof already returns: WORLD_STATE_UNAVAILABLE. @Override - protected String resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { + protected Object resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { final Address address; try { address = request.getRequiredParameter(0, Address.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid address parameter (index 0)", RpcErrorType.INVALID_ADDRESS_PARAMS, e); } - return getBlockchainQueries().getCode(address, blockHash).map(Bytes::toString).orElse(null); + return getBlockchainQueries() + .getCode(address, blockHash) + .map(Bytes::toString) + .map(s -> s) + .orElseGet( + () -> + new JsonRpcErrorResponse( + request.getRequest().getId(), RpcErrorType.WORLD_STATE_UNAVAILABLE)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java index 22de0da444..b6a3e95eec 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetStorageAt.java @@ -1,73 +1,89 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.parameters.UInt256Parameter; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.apache.tuweni.units.bigints.UInt256; public class EthGetStorageAt extends AbstractBlockParameterOrBlockHashMethod { public EthGetStorageAt(final BlockchainQueries blockchainQueries) { super(blockchainQueries); } @Override public String getName() { return RpcMethod.ETH_GET_STORAGE_AT.getMethodName(); } @Override protected BlockParameterOrBlockHash blockParameterOrBlockHash( final JsonRpcRequestContext request) { try { return request.getRequiredParameter(2, BlockParameterOrBlockHash.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid block or block hash parameter (index 2)", RpcErrorType.INVALID_BLOCK_PARAMS, e); } } @Override - protected String resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { + // AERE D-FAZA2-04 (2026-09-10): an EMPTY answer from the state query means the world state for that + // block is not available (outside the node's state window, or not yet persisted); it never means + // "no such account" - fromAccount() fills a default for that. Upstream serialised the empty answer as + // a bare JSON null, which every client reads as a value. Here it is the same honest error that + // eth_getProof already returns: WORLD_STATE_UNAVAILABLE. + protected Object resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { final Address address; try { address = request.getRequiredParameter(0, Address.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid address parameter (index 0)", RpcErrorType.INVALID_ADDRESS_PARAMS, e); } final UInt256 position; try { position = request.getRequiredParameter(1, UInt256Parameter.class).getValue(); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid position parameter (index 1)", RpcErrorType.INVALID_POSITION_PARAMS, e); } return blockchainQueries .get() .storageAt(address, position, blockHash) .map(UInt256::toHexString) - .orElse(null); + .map(s -> s) + .orElseGet( + () -> + new JsonRpcErrorResponse( + request.getRequest().getId(), RpcErrorType.WORLD_STATE_UNAVAILABLE)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java index 4e26f8ee98..291056a7af 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java @@ -1,43 +1,50 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import java.util.function.Supplier; import com.google.common.base.Suppliers; public class EthGetTransactionCount extends AbstractBlockParameterOrBlockHashMethod { private final Supplier transactionPoolSupplier; public EthGetTransactionCount( final BlockchainQueries blockchain, final TransactionPool transactionPoolSupplier) { this(Suppliers.ofInstance(blockchain), Suppliers.ofInstance(transactionPoolSupplier)); } public EthGetTransactionCount( final Supplier blockchain, final Supplier transactionPoolSupplier) { @@ -67,34 +74,46 @@ public class EthGetTransactionCount extends AbstractBlockParameterOrBlockHashMet try { address = request.getRequiredParameter(0, Address.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid address parameter (index 0)", RpcErrorType.INVALID_ADDRESS_PARAMS, e); } final long pendingNonce = transactionPoolSupplier.get().getNextNonceForSender(address).orElse(0); final long latestNonce = getBlockchainQueries() .getTransactionCount( address, getBlockchainQueries().getBlockchain().getChainHead().getHash()); if (Long.compareUnsigned(pendingNonce, latestNonce) > 0) { return Quantity.create(pendingNonce); } return Quantity.create(latestNonce); } + // AERE D-FAZA2-04 (2026-09-10): an EMPTY answer from the state query means the world state for that + // block is not available (outside the node's state window, or not yet persisted); it never means + // "no such account" - fromAccount() fills a default for that. Upstream serialised the empty answer as + // a bare JSON null, which every client reads as a value. Here it is the same honest error that + // eth_getProof already returns: WORLD_STATE_UNAVAILABLE. + // Upstream folded the missing state into a nonce of 0 (`.orElse(0L)`), a FALSE 0x0 that a wallet + // signs with. Here the nonce is asked for as an Optional and the missing state is an error. @Override - protected String resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { + protected Object resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { final Address address; try { address = request.getRequiredParameter(0, Address.class); } catch (JsonRpcParameterException e) { throw new InvalidJsonRpcParameters( "Invalid address parameter (index 0)", RpcErrorType.INVALID_ADDRESS_PARAMS, e); } - final long transactionCount = getBlockchainQueries().getTransactionCount(address, blockHash); - - return Quantity.create(transactionCount); + return getBlockchainQueries() + .getTransactionCountIfStateAvailable(address, blockHash) + .map(Quantity::create) + .map(s -> s) + .orElseGet( + () -> + new JsonRpcErrorResponse( + request.getRequest().getId(), RpcErrorType.WORLD_STATE_UNAVAILABLE)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index caa03d9ce8..b7e4c4524e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-drop-per-request-console-writes.patch. */ package org.hyperledger.besu.ethereum.api.query; import static com.google.common.base.Preconditions.checkArgument; import static org.hyperledger.besu.ethereum.api.query.cache.TransactionLogBloomCacher.BLOCKS_PER_BLOOM_CACHE; import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent; import static org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams.withBlockHeaderAndNoUpdateNodeHead; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.LogsBloomFilter; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; import org.hyperledger.besu.ethereum.api.query.cache.TransactionLogBloomCacher; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.TransactionLocation; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; @@ -320,40 +326,53 @@ public class BlockchainQueries { final Hash blockHash = getBlockHeaderByNumber(blockNumber).map(BlockHeader::getHash).orElse(Hash.EMPTY); return getTransactionCount(address, blockHash); } /** * Returns the number of transactions sent from the given address in the block at the given hash. * * @param address The address whose sent transactions we want to count. * @param blockHash The hash of the block being queried. * @return The number of transactions sent from the given address. */ public long getTransactionCount(final Address address, final Hash blockHash) { return getAndMapWorldState( blockHash, worldState -> Optional.ofNullable(worldState.get(address))) .map(Account::getNonce) .orElse(0L); } + /** + * AERE D-FAZA2-04: the nonce as an Optional that is EMPTY when the world state for the block is not + * available, so a caller can tell "state missing" from "nonce zero". {@link #getTransactionCount(Address, + * Hash)} keeps upstream's behaviour (0 when missing) for the callers that rely on it. + */ + public Optional getTransactionCountIfStateAvailable(final Address address, final Hash blockHash) { + return getAndMapWorldState( + blockHash, + worldState -> + Optional.of( + Optional.ofNullable(worldState.get(address)).map(Account::getNonce).orElse(0L))); + } + /** * Returns the number of transactions sent from the given address in the latest block. * * @param address The address whose sent transactions we want to count. * @return The number of transactions sent from the given address. */ public long getTransactionCount(final Address address) { return getTransactionCount(address, headBlockNumber()); } /** * Returns the number of ommers in the block at the given height. * * @param blockNumber The height of the block being queried. * @return The number of ommers in the referenced block. */ public Optional getOmmerCount(final long blockNumber) { return blockchain.getBlockHashByNumber(blockNumber).flatMap(this::getOmmerCount); } @@ -905,41 +924,41 @@ public class BlockchainQueries { for (long pos = offset; pos <= endOffset; pos++) { BackendQuery.stopIfExpired(isQueryAlive); try { raf.readFully(bloomBuff); } catch (final EOFException e) { results.addAll( matchingLogsUncached( segmentStart + pos, segmentStart + endOffset, query, isQueryAlive)); break; } final LogsBloomFilter logsBloom = new LogsBloomFilter(bytesValue); if (query.couldMatch(logsBloom)) { results.addAll( matchingLogs( blockchain.getBlockHashByNumber(segmentStart + pos).orElseThrow(), query, isQueryAlive)); } } } catch (final IOException e) { - e.printStackTrace(System.out); + // AERE D-434 (fbfb5f9ecd): removed printStackTrace(System.out) on the cached-log-blooms error path; a cheap malformed log query could otherwise spam the process console, bypassing log4j. LOG.error below records the exception. LOG.error("Error reading cached log blooms", e); } return results; } public List matchingLogs( final Hash blockHash, final LogsQuery query, final Supplier isQueryAlive) { try { final Optional blockHeader = getBlockHeader(blockHash, isQueryAlive); if (blockHeader.isEmpty()) { return Collections.emptyList(); } // receipts and transactions should exist if the header exists, so throwing is ok. final List receipts = getReceipts(blockHash, isQueryAlive); final List transactions = getTransactions(blockHash, isQueryAlive); final long number = blockHeader.get().getNumber(); final long blockTimestamp = blockHeader.get().getTimestamp(); final boolean removed = getRemoved(blockHash, isQueryAlive); final AtomicInteger logIndexOffset = new AtomicInteger(); @@ -998,76 +1017,119 @@ public class BlockchainQueries { final int transactionIndex = transactionWithMetaData.getTransactionIndex().get(); final int logIndexOffset = logIndexOffset( transactionWithMetaData.getTransaction().getHash(), receipts, transactions); return LogWithMetadata.generate( logIndexOffset, receipts.get(transactionIndex), number, blockHash, blockTimestamp, transactions.get(transactionIndex).getHash(), transactionIndex, removed); } catch (final Exception e) { throw new RuntimeException(e); } } + // AERE D-109 (2026-09-10): the world state of a block that has JUST become the chain head is not + // available for a few milliseconds (measured on the live readers: 2-96 ms, occasionally >300 ms) + // after its header is; a query resolved against 'latest' in that window used to return + // Optional.empty, which JSON-RPC serialises as a bare null - a value Besu never returns otherwise + // for eth_getCode/getBalance/getStorageAt/getTransactionCount. Measured directly on a reader: + // ~1 in 5,000 'latest' queries, always within a few ms of an import, never for older blocks. So + // when the lookup fails for a header at (or within HEAD_WINDOW blocks of) the chain head, we look + // again a bounded number of times, with short pauses, before giving up. Older blocks are not + // retried: for them an empty result is the state window closing, and a retry would only add latency. + private static final long AERE_HEAD_WINDOW_BLOCKS = 2; + private static final long[] AERE_HEAD_STATE_RETRY_PAUSES_MS = {50, 100, 150, 300}; + + /** * Wraps an operation on MutableWorldState with try-with-resources the corresponding block hash. * This method provides access to the worldstate via a mapper function in order to ensure all uses * of the MutableWorldState are subsequently closed, via the try-with-resources block. * * @param return type of the operation on the MutableWorldState * @param blockHash the block hash * @param mapper Function which performs an operation on a MutableWorldState * @return the world state at the block number */ public Optional getAndMapWorldState( final Hash blockHash, final Function> mapper) { return blockchain .getBlockHeader(blockHash) .flatMap( blockHeader -> { - try (var ws = - worldStateArchive - .getWorldState(withBlockHeaderAndNoUpdateNodeHead(blockHeader)) - .orElse(null)) { - if (ws != null) { - return mapper.apply(ws); + int attempts = 0; + while (true) { + try (var ws = + worldStateArchive + .getWorldState(withBlockHeaderAndNoUpdateNodeHead(blockHeader)) + .orElse(null)) { + if (ws != null) { + if (attempts > 0) { + LOG.info( + "AERE-D109: world state for {} became available after {} retr{}", + blockHeader.toLogString(), + attempts, + attempts == 1 ? "y" : "ies"); + } + return mapper.apply(ws); + } + } catch (Exception ex) { + LOG.error( + "failed worldstate query for " + blockHash.getBytes().toShortHexString(), ex); + return Optional.empty(); + } + if (!aereShouldRetryHeadState(blockHeader, attempts)) { + break; + } + try { + Thread.sleep(AERE_HEAD_STATE_RETRY_PAUSES_MS[attempts]); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; } - } catch (Exception ex) { - LOG.error( - "failed worldstate query for " + blockHash.getBytes().toShortHexString(), ex); + attempts++; } LOG.atDebug() .setMessage("Failed to find worldstate for {}") .addArgument(blockHeader.toLogString()) .log(); return Optional.empty(); }); } + // AERE D-109: retry only near the head, and only a bounded number of times. + boolean aereShouldRetryHeadState(final BlockHeader blockHeader, final int attempts) { + if (attempts >= AERE_HEAD_STATE_RETRY_PAUSES_MS.length) { + return false; + } + final long head = blockchain.getChainHeadBlockNumber(); + return head - blockHeader.getNumber() <= AERE_HEAD_WINDOW_BLOCKS; + } + /** * Wraps an operation on MutableWorldState with try-with-resources the corresponding block number * * @param return type of the operation on the MutableWorldState * @param blockNumber the block number * @param mapper Function which performs an operation on a MutableWorldState returning type U * @return the world state at the block number */ public Optional getAndMapWorldState( final long blockNumber, final Function> mapper) { final Hash blockHash = getBlockHeaderByNumber(blockNumber).map(BlockHeader::getHash).orElse(Hash.EMPTY); return getAndMapWorldState(blockHash, mapper); } public Wei gasPrice() { final Block chainHeadBlock = blockchain.getChainHeadBlock(); final var chainHeadHeader = chainHeadBlock.getHeader(); final long blockHeight = chainHeadHeader.getNumber(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AereStateUnavailableIsAnErrorTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AereStateUnavailableIsAnErrorTest.java new file mode 100755 index 0000000000..d2caccd124 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AereStateUnavailableIsAnErrorTest.java @@ -0,0 +1,138 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.ethereum.api.jsonrpc.internal.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * AERE D-FAZA2-04: when the world state for the requested block is not available, the four state + * methods answer with the WORLD_STATE_UNAVAILABLE error, never with a bare null (or a nonce of 0). + * When the state IS available they answer as before. + */ +public class AereStateUnavailableIsAnErrorTest { + + private static final Address ADDR = Address.fromHexString("0x1111111111111111111111111111111111111111"); + private static final Hash BLOCK_HASH = Hash.fromHexStringLenient("0xabcd"); + private static final long HEAD = 1000L; + + private BlockchainQueries queries; + + @BeforeEach + public void setUp() { + queries = mock(BlockchainQueries.class); + // the numeric block parameter path: number -> hash + when(queries.headBlockNumber()).thenReturn(HEAD); + when(queries.getBlockHashByNumber(HEAD)).thenReturn(Optional.of(BLOCK_HASH)); + final BlockHeader header = mock(BlockHeader.class); + when(header.getBlockHash()).thenReturn(BLOCK_HASH); + when(queries.getBlockHeaderByNumber(HEAD)).thenReturn(Optional.of(header)); + when(queries.getBlockHeaderByHash(BLOCK_HASH)).thenReturn(Optional.of(header)); + } + + private static JsonRpcRequestContext request(final String method, final Object... params) { + return new JsonRpcRequestContext(new JsonRpcRequest("2.0", method, params)); + } + + private static void assertWorldStateUnavailable(final JsonRpcResponse response) { + assertThat(response).isInstanceOf(JsonRpcErrorResponse.class); + assertThat(((JsonRpcErrorResponse) response).getErrorType()) + .isEqualTo(RpcErrorType.WORLD_STATE_UNAVAILABLE); + } + + private static Object result(final JsonRpcResponse response) { + assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class); + return ((JsonRpcSuccessResponse) response).getResult(); + } + + @Test + public void getCodeWithoutStateIsAnError() { + when(queries.getCode(any(Address.class), any(Hash.class))).thenReturn(Optional.empty()); + final EthGetCode method = new EthGetCode(queries); + assertWorldStateUnavailable(method.response(request("eth_getCode", ADDR.toHexString(), "0x3e8"))); + } + + @Test + public void getCodeWithStateAnswersAsBefore() { + when(queries.getCode(any(Address.class), any(Hash.class))).thenReturn(Optional.of(Bytes.fromHexString("0x6001"))); + final EthGetCode method = new EthGetCode(queries); + assertThat(result(method.response(request("eth_getCode", ADDR.toHexString(), "0x3e8")))).isEqualTo("0x6001"); + } + + @Test + public void getBalanceWithoutStateIsAnError() { + when(queries.accountBalance(any(Address.class), any(Hash.class))).thenReturn(Optional.empty()); + final EthGetBalance method = new EthGetBalance(queries); + assertWorldStateUnavailable(method.response(request("eth_getBalance", ADDR.toHexString(), "0x3e8"))); + } + + @Test + public void getBalanceWithStateAnswersAsBefore() { + when(queries.accountBalance(any(Address.class), any(Hash.class))).thenReturn(Optional.of(Wei.of(7))); + final EthGetBalance method = new EthGetBalance(queries); + assertThat(result(method.response(request("eth_getBalance", ADDR.toHexString(), "0x3e8")))).isEqualTo("0x7"); + } + + @Test + public void getStorageAtWithoutStateIsAnError() { + when(queries.storageAt(any(Address.class), any(UInt256.class), any(Hash.class))).thenReturn(Optional.empty()); + final EthGetStorageAt method = new EthGetStorageAt(queries); + assertWorldStateUnavailable(method.response(request("eth_getStorageAt", ADDR.toHexString(), "0x0", "0x3e8"))); + } + + @Test + public void getStorageAtWithStateAnswersAsBefore() { + when(queries.storageAt(any(Address.class), any(UInt256.class), any(Hash.class))).thenReturn(Optional.of(UInt256.valueOf(5))); + final EthGetStorageAt method = new EthGetStorageAt(queries); + assertThat(result(method.response(request("eth_getStorageAt", ADDR.toHexString(), "0x0", "0x3e8")))) + .isEqualTo(UInt256.valueOf(5).toHexString()); + } + + @Test + public void getTransactionCountWithoutStateIsAnErrorNotZero() { + when(queries.getTransactionCountIfStateAvailable(any(Address.class), any(Hash.class))).thenReturn(Optional.empty()); + final EthGetTransactionCount method = new EthGetTransactionCount(queries, mock(TransactionPool.class)); + assertWorldStateUnavailable(method.response(request("eth_getTransactionCount", ADDR.toHexString(), "0x3e8"))); + } + + @Test + public void getTransactionCountWithStateAnswersAsBefore() { + when(queries.getTransactionCountIfStateAvailable(any(Address.class), any(Hash.class))).thenReturn(Optional.of(3L)); + final EthGetTransactionCount method = new EthGetTransactionCount(queries, mock(TransactionPool.class)); + assertThat(result(method.response(request("eth_getTransactionCount", ADDR.toHexString(), "0x3e8")))).isEqualTo("0x3"); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/AereHeadStateRetryTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/AereHeadStateRetryTest.java new file mode 100755 index 0000000000..33be192f65 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/AereHeadStateRetryTest.java @@ -0,0 +1,140 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.ethereum.api.query; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +/** + * AERE D-109: a query against a block that has just become the chain head can find no world state + * for a few milliseconds. {@link BlockchainQueries#getAndMapWorldState(Hash, java.util.function.Function)} + * must look again (a bounded number of times) near the head, and must NOT retry for old blocks, + * where an empty result is the state window closing. + */ +public class AereHeadStateRetryTest { + + private static final Hash HASH = Hash.fromHexStringLenient("0x1234"); + + private static BlockHeader headerAt(final long number) { + final BlockHeader header = mock(BlockHeader.class); + when(header.getNumber()).thenReturn(number); + when(header.getHash()).thenReturn(HASH); + when(header.getBlockHash()).thenReturn(HASH); + when(header.getStateRoot()).thenReturn(Hash.EMPTY_TRIE_HASH); + when(header.getParentHash()).thenReturn(Hash.ZERO); + when(header.toLogString()).thenReturn("#" + number); + return header; + } + + private static BlockchainQueries queries( + final Blockchain blockchain, final WorldStateArchive archive) { + return new BlockchainQueries( + mock(ProtocolSchedule.class), blockchain, archive, MiningConfiguration.newDefault()); + } + + @Test + public void headBlockStateThatAppearsOnSecondLookIsReturned() { + final Blockchain blockchain = mock(Blockchain.class); + final WorldStateArchive archive = mock(WorldStateArchive.class); + final BlockHeader head = headerAt(1000); + when(blockchain.getBlockHeader(HASH)).thenReturn(Optional.of(head)); + when(blockchain.getChainHeadBlockNumber()).thenReturn(1000L); + final MutableWorldState ws = mock(MutableWorldState.class); + when(archive.getWorldState(any(WorldStateQueryParams.class))) + .thenReturn(Optional.empty()) + .thenReturn(Optional.of(ws)); + + final Optional result = + queries(blockchain, archive).getAndMapWorldState(HASH, w -> Optional.of("ok")); + + assertThat(result).contains("ok"); + verify(archive, times(2)).getWorldState(any(WorldStateQueryParams.class)); + } + + @Test + public void oldBlockWithoutStateIsNotRetried() { + // negative control: an old block gets exactly ONE look, and an empty answer stays empty + final Blockchain blockchain = mock(Blockchain.class); + final WorldStateArchive archive = mock(WorldStateArchive.class); + final BlockHeader old = headerAt(1000); + when(blockchain.getBlockHeader(HASH)).thenReturn(Optional.of(old)); + when(blockchain.getChainHeadBlockNumber()).thenReturn(5000L); + when(archive.getWorldState(any(WorldStateQueryParams.class))).thenReturn(Optional.empty()); + + final Optional result = + queries(blockchain, archive).getAndMapWorldState(HASH, w -> Optional.of("ok")); + + assertThat(result).isEmpty(); + verify(archive, times(1)).getWorldState(any(WorldStateQueryParams.class)); + } + + @Test + public void headBlockThatNeverGetsStateStopsAfterBoundedRetries() { + // the retry is bounded: a head block that never gets its state is asked 1 + 4 times, then empty + final Blockchain blockchain = mock(Blockchain.class); + final WorldStateArchive archive = mock(WorldStateArchive.class); + final BlockHeader head = headerAt(1000); + when(blockchain.getBlockHeader(HASH)).thenReturn(Optional.of(head)); + when(blockchain.getChainHeadBlockNumber()).thenReturn(1001L); + when(archive.getWorldState(any(WorldStateQueryParams.class))).thenReturn(Optional.empty()); + + final long t0 = System.nanoTime(); + final Optional result = + queries(blockchain, archive).getAndMapWorldState(HASH, w -> Optional.of("ok")); + final long elapsedMs = (System.nanoTime() - t0) / 1_000_000; + + assertThat(result).isEmpty(); + verify(archive, times(5)).getWorldState(any(WorldStateQueryParams.class)); + // 50 + 100 + 150 + 300 ms of pauses, and nothing unbounded + assertThat(elapsedMs).isBetween(550L, 5000L); + } + + @Test + public void stateFoundOnFirstLookIsReturnedWithoutWaiting() { + final Blockchain blockchain = mock(Blockchain.class); + final WorldStateArchive archive = mock(WorldStateArchive.class); + final BlockHeader head = headerAt(1000); + when(blockchain.getBlockHeader(HASH)).thenReturn(Optional.of(head)); + when(blockchain.getChainHeadBlockNumber()).thenReturn(1000L); + when(archive.getWorldState(any(WorldStateQueryParams.class))) + .thenReturn(Optional.of(mock(MutableWorldState.class))); + + final long t0 = System.nanoTime(); + final Optional result = + queries(blockchain, archive).getAndMapWorldState(HASH, w -> Optional.of("ok")); + final long elapsedMs = (System.nanoTime() - t0) / 1_000_000; + + assertThat(result).contains("ok"); + verify(archive, times(1)).getWorldState(any(WorldStateQueryParams.class)); + assertThat(elapsedMs).isLessThan(50L); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index e765d882f2..3acaea6ba6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -1,47 +1,39 @@ /* * Copyright contributors to Besu. * * 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 - */ - -/* - * Modifications Copyright 2026 Aere Network. * - * This file was changed by Aere Network: the futureEips milestone is based on - * osakaDefinition instead of amsterdamDefinition, so that the AERE activation at - * block 9,189,161 keeps the Osaka block format, and EIP-2935 (historical block - * hashes in the system contract) is enabled in that same milestone. Nothing - * upstream was removed; the unmodified original is Hyperledger Besu commit - * d2032017bb3b8cb215a97303980a1e4a643f7180, and the upstream copyright header - * above is left exactly as it was found. + * Modifications Copyright contributors to the Aere Network. * - * This notice is required by Apache License 2.0 section 4(b). + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0005-aere-eip2935-futureeips.patch. */ package org.hyperledger.besu.ethereum.mainnet; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.AMSTERDAM; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.ARROW_GLACIER; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BERLIN; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BPO1; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BPO2; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BPO3; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BPO4; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BPO5; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.BYZANTIUM; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.CANCUN; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.CONSTANTINOPLE; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.DAO_RECOVERY_INIT; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.DAO_RECOVERY_TRANSITION; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.EXPERIMENTAL_EIPS; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.FRONTIER; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.FUTURE_EIPS; import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.GRAY_GLACIER; @@ -1347,40 +1339,52 @@ public abstract class MainnetProtocolSpecs { // the post-activation block format IDENTICAL to Osaka and adds ONLY the two // AERE features below: (a) the 5 PQC precompiles and (b) EIP-2935. return osakaDefinition( chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningConfiguration, isParallelTxProcessingEnabled, balConfiguration, metricsSystem) // AERE EIP-2935: historical block hashes (ring buffer in the system // contract at 0x0000f9...2935), wired into the SAME futureEips (AerePQC) // hard fork as the PQC precompiles so a single futureEipsTime activation // delivers both. Forced on unconditionally here: the inherited Prague // definition falls back to FrontierPreExecutionProcessor for QBFT/PoA // chains without system-contract addresses, which would silently skip // EIP-2935 history storage. This override restores it, protocol-native. .preExecutionProcessor(new PraguePreExecutionProcessor()) .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::futureEips) + // AERE AerePQC fork also activates Block-STM optimistic parallel execution when + // AERE_BLOCKSTM=1, so one hard fork delivers PQC + EIP-2935 + parallel exec together. + // When unset this reproduces the inherited default builder exactly (live-safe). + .blockProcessorBuilder( + "1".equals(System.getenv("AERE_BLOCKSTM")) + ? new org.hyperledger.besu.ethereum.mainnet.blockstm.AereBlockStmProcessor + .AereBlockStmProcessorBuilder(metricsSystem) + : (isParallelTxProcessingEnabled + ? new org.hyperledger.besu.ethereum.mainnet.parallelization + .MainnetParallelBlockProcessor.ParallelBlockProcessorBuilder(metricsSystem) + : new org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor + .MainnetBlockProcessorBuilder(metricsSystem))) .hardforkId(FUTURE_EIPS); } static ProtocolSpecBuilder experimentalEipsDefinition( final Optional chainId, final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, final MiningConfiguration miningConfiguration, final boolean isParallelTxProcessingEnabled, final BalConfiguration balConfiguration, final MetricsSystem metricsSystem) { return futureEipsDefinition( chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningConfiguration, isParallelTxProcessingEnabled, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java old mode 100644 new mode 100755 index 4c1d265a80..804f4bed0a --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -1,39 +1,39 @@ /* * Copyright contributors to Hyperledger Besu. * * 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 * * Modifications Copyright contributors to the Aere Network. * * This file was modified by contributors to the Aere Network, as required by section 4(b) of the * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was - * found, as section 4(c) requires. The change itself is in patches/0006-aere-pq-transactions.patch. + * found, as section 4(c) requires. The change itself is in patches/aere-tx-gas-cap-unsigned.patch. */ package org.hyperledger.besu.ethereum.mainnet; import static org.hyperledger.besu.evm.account.Account.MAX_NONCE; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.hasCodeDelegation; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.BlobType; import org.hyperledger.besu.datatypes.CodeDelegation; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -99,41 +99,43 @@ public class MainnetTransactionValidator implements TransactionValidator { validateTransactionSignature(transaction); if (!signatureResult.isValid()) { return signatureResult; } final TransactionType transactionType = transaction.getType(); if (!acceptedTransactionTypes.contains(transactionType)) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_TRANSACTION_FORMAT, String.format( "Transaction type %s is invalid, accepted transaction types are %s", transactionType, acceptedTransactionTypes)); } if (!transactionValidationParams.isAllowFutureNonce() && transaction.getNonce() == MAX_NONCE) { return ValidationResult.invalid( TransactionInvalidReason.NONCE_OVERFLOW, "Nonce must be less than 2^64-1"); } if (!transactionValidationParams.isAllowExceedingGasLimit() - && transaction.getGasLimit() > gasLimitCalculator.transactionGasLimitCap()) { + // AERE D-432: unsigned, like the intrinsic-gas check below. The decoder reads gasLimit with readLongScalar, so a limit of 2^63 + // or more arrives as a negative long, and a signed compare let it through the EIP-7825 cap. + && Long.compareUnsigned(transaction.getGasLimit(), gasLimitCalculator.transactionGasLimitCap()) > 0) { return ValidationResult.invalid( TransactionInvalidReason.EXCEEDS_TRANSACTION_GAS_LIMIT, "Transaction gas limit must be at most " + gasLimitCalculator.transactionGasLimitCap()); } if (transactionType.supportsBlob()) { final ValidationResult blobTransactionResult = blobsValidator.validate(transaction); if (!blobTransactionResult.isValid()) { LOG.debug( "Blob transaction {} validation failed: {}", transaction.getHash().getBytes().toHexString(), blobTransactionResult.getErrorMessage()); return blobTransactionResult; } } if (transaction.isContractCreation() && transaction.getPayload().size() > maxInitcodeSize) { return ValidationResult.invalid( TransactionInvalidReason.INITCODE_TOO_LARGE, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsValidator.java old mode 100644 new mode 100755 index 2adda62054..fc04a7c55b --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsValidator.java @@ -1,33 +1,39 @@ /* * Copyright contributors to Hyperledger Besu. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-withdrawals-not-applicable.patch. */ package org.hyperledger.besu.ethereum.mainnet; import static com.google.common.base.Preconditions.checkArgument; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Withdrawal; import java.util.List; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public interface WithdrawalsValidator { boolean validateWithdrawals(Optional> withdrawals); boolean validateWithdrawalsRoot(Block block); @@ -82,31 +88,62 @@ public interface WithdrawalsValidator { LOG.warn("withdrawalsRoot must not be null when Withdrawals are activated"); return false; } final List withdrawals = block.getBody().getWithdrawals().get(); final Hash expectedWithdrawalsRoot = BodyValidation.withdrawalsRoot(withdrawals); if (!expectedWithdrawalsRoot.equals(withdrawalsRoot.get())) { LOG.info( "Invalid block: transaction root mismatch (expected={}, actual={})", expectedWithdrawalsRoot, withdrawalsRoot.get()); return false; } return true; } } class NotApplicableWithdrawals implements WithdrawalsValidator { + private static final Logger LOG = LoggerFactory.getLogger(NotApplicableWithdrawals.class); + + // AERE D-433: ported from upstream hyperledger/besu ffe8104ee2 (GHSA-p4h2-gvh4-pv6j) and b7f1387cb4. On a BFT chain a non-empty list is a + // Byzantine proposer's injection that WithdrawalsProcessor would credit as native balance on every honest node. @Override public boolean validateWithdrawals(final Optional> withdrawals) { + // Pre-Shanghai blocks have no withdrawals field (Optional.empty()) - valid. + // Shanghai+ blocks must carry an empty list; a non-empty list means a Byzantine + // proposer injected unauthorized withdrawals that would be executed as balance credits. + if (withdrawals.isPresent() && !withdrawals.get().isEmpty()) { + LOG.warn( + "Withdrawals not applicable but block contained {} withdrawal(s)", + withdrawals.get().size()); + return false; + } return true; } @Override public boolean validateWithdrawalsRoot(final Block block) { + final Optional withdrawalsRoot = block.getHeader().getWithdrawalsRoot(); + if (withdrawalsRoot.isEmpty()) { + return true; // pre-Shanghai block, no withdrawals root field + } + // A block with a withdrawalsRoot in the header but no withdrawals field in the body is + // malformed. + if (block.getBody().getWithdrawals().isEmpty()) { + LOG.warn( + "Invalid block: withdrawalsRoot present in header but withdrawals absent from body"); + return false; + } + if (!Hash.EMPTY_TRIE_HASH.equals(withdrawalsRoot.get())) { + LOG.warn( + "Invalid block: withdrawals root mismatch (expected={}, actual={})", + Hash.EMPTY_TRIE_HASH, + withdrawalsRoot.get()); + return false; + } return true; } } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmEngine.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmEngine.java new file mode 100644 index 0000000000..1ef6489405 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmEngine.java @@ -0,0 +1,528 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder; +import org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockTransactionProcessor; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.plugin.services.metrics.Counter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AERE Block-STM optimistic parallel execution engine, wired into Besu's block-processing + * preprocessing hook. Faithful realisation of the AERE Rust Block-STM (Gelashvili et al. 2022): + * {@link Scheduler} + {@link MvMemory} drive a worker pool that runs the block's transactions + * concurrently against a multi-version world view ({@link MvBaseLayer}/{@link MvAccount}), captures + * read/write sets, re-executes conflicting transactions, and yields per-transaction outcomes that + * the block processor commits IN BLOCK ORDER — producing a state root bit-identical to sequential + * execution. + * + *

    Safety: any transaction whose parallel outcome is missing, unsuccessful, or produced by a run + * that did not reach {@code scheduler.done()} falls back to Besu's sequential execution in the block + * loop (see {@link #getProcessingResult}). The engine can never commit an unverified result. + */ +@SuppressWarnings("MissingSummary") +public class AereBlockStmEngine extends ParallelBlockTransactionProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(AereBlockStmEngine.class); + private static final long PARALLEL_TIMEOUT_SECONDS = 60; + + private final MainnetTransactionProcessor transactionProcessor; + private final int numThreads; + + // Populated by runAsyncBlock, consumed in block order by getProcessingResult. + private volatile MvOutcome[] outcomes; + private volatile boolean success = false; + private final AtomicInteger totalExecutions = new AtomicInteger(); + private final AtomicInteger totalValidations = new AtomicInteger(); + private final AtomicInteger totalAborts = new AtomicInteger(); + private final AtomicInteger committedParallel = new AtomicInteger(); + + // Execution-phase instrumentation (see BlockStmStats): snapshotNanos = time to mint the single + // parent Bonsai view + flat pre-snapshot wrapper; execPhaseNanos = the PARALLEL EXECUTION PHASE + // (worker pool start -> termination) in isolation. Neither includes the downstream serial Bonsai + // trie commit, which happens in the block processor after this preprocessing returns. + private volatile long snapshotNanos = 0L; + private volatile long execPhaseNanos = 0L; + + /** + * @param transactionProcessor the mainnet transaction processor (EVM) + * @param numThreads worker-pool size (typically NCPU) + */ + public AereBlockStmEngine( + final MainnetTransactionProcessor transactionProcessor, final int numThreads) { + this.transactionProcessor = transactionProcessor; + this.numThreads = Math.max(1, numThreads); + } + + /** Per-transaction net outcome captured by the parallel phase. */ + private static final class MvOutcome { + final Map writes; + final Set writeKeys; + final List>> storageByAddr; + final Map nonceByAddr; // addr -> [nonce] if written + final Map balanceByAddr; // addr -> balance if written + final Map codeByAddr; // addr -> code if written + final Set

    deleted; + Wei beneficiaryDelta = Wei.ZERO; + TransactionProcessingResult result; + + MvOutcome() { + this.writes = new HashMap<>(); + this.writeKeys = new HashSet<>(); + this.storageByAddr = new ArrayList<>(); + this.nonceByAddr = new HashMap<>(); + this.balanceByAddr = new HashMap<>(); + this.codeByAddr = new HashMap<>(); + this.deleted = new HashSet<>(); + } + } + + // ---- shared per-block execution state ---- + private MvMemory mv; + private Scheduler scheduler; + private List txns; + private BlockHeader blockHeader; + private Address beneficiary; + private BlockHashLookup blockHashLookup; + private Wei blobGasPrice; + private Object[] slotLocks; + private Map[] lastReads; + private Set[] lastWriteKeys; + private final AtomicBoolean hardFail = new AtomicBoolean(false); + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public void runAsyncBlock( + final ProtocolContext protocolContext, + final BlockHeader header, + final List transactions, + final Address miningBeneficiary, + final BlockHashLookup blockHashLookupParam, + final Wei blobGasPriceParam, + final java.util.concurrent.Executor executorUnused, + final Optional blockAccessListBuilder, + final Optional maybeParentHeader) { + + final int n = transactions.size(); + outcomes = new MvOutcome[n]; + success = false; + if (n == 0 || maybeParentHeader.isEmpty()) { + return; + } + + // AERE Block-STM Track B: FLAT immutable base pre-snapshot shared lock-free by all workers. + // Iteration 1 gave each worker its OWN frozen BonsaiWorldState and re-walked the parent trie on + // every base read (task bottleneck #2: per-worker Bonsai-view minting + layered-trie traversal + // per read). Here we mint the parent Bonsai view exactly ONCE, wrap it in a FlatBaseSnapshot + // that materialises each base value ONCE into an in-memory ConcurrentHashMap, and hand the SAME + // snapshot to every worker. Hot-path base reads become lock-free O(1) hashmap lookups with no + // trie traversal and no per-worker view. The base state is immutable for the round, so all + // workers observe identical parent values and serial equivalence holds (parallelRoot == + // sequentialRoot). snapshotNanos captures this one-time mint separately from execPhaseNanos. + final long snapT0 = System.nanoTime(); + final List openStates = new ArrayList<>(1); + try { + final BonsaiWorldState ws = + getWorldState(protocolContext, maybeParentHeader.get()).orElse(null); + if (ws == null) { + return; // success stays false -> sequential fallback + } + ws.disableCacheMerkleTrieLoader(); + openStates.add(ws); + final BaseReader sharedBase = new FlatBaseSnapshot(ws); + this.snapshotNanos = System.nanoTime() - snapT0; + + this.mv = new MvMemory(); + this.scheduler = new Scheduler(n); + this.txns = transactions; + this.blockHeader = header; + this.beneficiary = miningBeneficiary; + this.blockHashLookup = blockHashLookupParam; + this.blobGasPrice = blobGasPriceParam; + this.slotLocks = new Object[n]; + this.lastReads = new Map[n]; + this.lastWriteKeys = new Set[n]; + for (int i = 0; i < n; i++) { + slotLocks[i] = new Object(); + lastWriteKeys[i] = new HashSet<>(); + } + hardFail.set(false); + + // EXECUTION PHASE (timed in isolation from the snapshot mint above and from the downstream + // serial Bonsai trie commit that runs after this method returns). All workers share the one + // FlatBaseSnapshot; its hot-path reads are lock-free, so distinct-key base reads never contend. + final long execT0 = System.nanoTime(); + final ExecutorService pool = Executors.newFixedThreadPool(numThreads); + try { + for (int t = 0; t < numThreads; t++) { + pool.execute(() -> worker(sharedBase)); + } + pool.shutdown(); + final boolean finished = pool.awaitTermination(PARALLEL_TIMEOUT_SECONDS, TimeUnit.SECONDS); + if (!finished) { + pool.shutdownNow(); + } + this.execPhaseNanos = System.nanoTime() - execT0; + success = finished && scheduler.done() && !hardFail.get(); + } catch (final InterruptedException ie) { + Thread.currentThread().interrupt(); + pool.shutdownNow(); + this.execPhaseNanos = System.nanoTime() - execT0; + success = false; + } + if (!success) { + LOG.info( + "AERE Block-STM: parallel phase did not verify (done={}, hardFail={}); block falls back" + + " to sequential per-transaction execution", + scheduler.done(), + hardFail.get()); + } + } catch (final RuntimeException ex) { + LOG.info("AERE Block-STM: parallel preprocessing error; sequential fallback", ex); + success = false; + } finally { + for (final BonsaiWorldState ws : openStates) { + try { + ws.close(); + } catch (final Exception ignored) { + // best-effort close + } + } + } + } + + private void worker(final BaseReader base) { + try { + Scheduler.Task task = scheduler.nextTask(); + while (!hardFail.get()) { + switch (task.kind()) { + case DONE: + return; + case NO_TASK: + task = scheduler.nextTask(); + break; + case EXECUTION: + task = tryExecute(base, task.idx(), task.inc()); + break; + case VALIDATION: + task = tryValidate(task.idx(), task.inc()); + break; + default: + return; + } + } + } catch (final Throwable th) { + hardFail.set(true); + LOG.debug("AERE Block-STM worker aborted", th); + } + } + + private Scheduler.Task tryExecute(final BaseReader base, final int idx, final int inc) { + while (true) { + if (hardFail.get()) { + return Scheduler.Task.NO_TASK; + } + final MvContext ctx = new MvContext(idx, mv, base, beneficiary); + final MvBaseLayer baseLayer = new MvBaseLayer(ctx); + final WorldUpdater txUpdater = baseLayer.updater(); + totalExecutions.incrementAndGet(); + + TransactionProcessingResult res = null; + try { + res = + transactionProcessor.processTransaction( + txUpdater, + blockHeader, + txns.get(idx).detachedCopy(), + beneficiary, + OperationTracer.NO_TRACING, + blockHashLookup, + TransactionValidationParams.processingBlock(), + blobGasPrice, + Optional.empty()); + } catch (final RuntimeException ex) { + if (ctx.blockedOn < 0) { + throw ex; // genuine failure -> worker aborts -> sequential fallback + } + // else: a routed read parked; fall through to dependency handling + } + + if (ctx.blockedOn >= 0) { + final int blocking = ctx.blockedOn; + if (scheduler.addDependency(idx, blocking)) { + return Scheduler.Task.NO_TASK; // parked + } + continue; // dependency already resolved; re-run same incarnation + } + + final MvOutcome outcome = harvest(ctx, baseLayer, txUpdater, res); + final Set prevKeys; + synchronized (slotLocks[idx]) { + prevKeys = lastWriteKeys[idx]; + } + final boolean wroteNewPath = mv.applyWriteSet(idx, inc, outcome.writes, prevKeys); + synchronized (slotLocks[idx]) { + lastReads[idx] = ctx.reads; + lastWriteKeys[idx] = outcome.writeKeys; + outcomes[idx] = outcome; + } + return scheduler.finishExecution(idx, inc, wroteNewPath); + } + } + + private Scheduler.Task tryValidate(final int idx, final int inc) { + totalValidations.incrementAndGet(); + final Map reads; + synchronized (slotLocks[idx]) { + reads = lastReads[idx]; + } + boolean valid = true; + if (reads != null) { + for (final Map.Entry e : reads.entrySet()) { + final MvMemory.ReadResult now = mv.read(e.getKey(), idx); + final boolean matches = !now.isBlocked() && e.getValue().sameProvenance(now.descriptor()); + if (!matches) { + valid = false; + break; + } + } + } + final boolean aborted = !valid && scheduler.tryValidationAbort(idx, inc); + if (aborted) { + totalAborts.incrementAndGet(); + final Set keys; + synchronized (slotLocks[idx]) { + keys = lastWriteKeys[idx]; + } + if (keys != null) { + mv.markEstimates(idx, keys); + } + } + return scheduler.finishValidation(idx, aborted); + } + + private MvOutcome harvest( + final MvContext ctx, + final MvBaseLayer baseLayer, + final WorldUpdater txUpdater, + final TransactionProcessingResult res) { + final MvOutcome o = new MvOutcome(); + o.result = res; + + for (final Account acct : txUpdater.getTouchedAccounts()) { + final Address a = acct.getAddress(); + final MvAccount seed = baseLayer.materializedAccount(a); + final long seedNonce = seed == null ? 0L : seed.seededNonce(); + final Wei seedBalance = seed == null ? Wei.ZERO : seed.seededBalance(); + final Hash seedCodeHash = seed == null ? Hash.EMPTY : seed.seededCodeHash(); + + final long nonce = acct.getNonce(); + if (nonce != seedNonce) { + o.nonceByAddr.put(a, new long[] {nonce}); + final MvKey k = MvKey.nonce(a); + o.writes.put(k, Long.valueOf(nonce)); + o.writeKeys.add(k); + } + + if (ctx.isBeneficiary(a)) { + final Wei bal = acct.getBalance(); + o.beneficiaryDelta = bal.subtract(seedBalance); + } else { + final Wei bal = acct.getBalance(); + if (!bal.equals(seedBalance)) { + o.balanceByAddr.put(a, bal); + final MvKey k = MvKey.balance(a); + o.writes.put(k, bal); + o.writeKeys.add(k); + } + } + + final Hash codeHash = acct.getCodeHash(); + if (!codeHash.equals(seedCodeHash)) { + final Bytes code = acct.getCode(); + o.codeByAddr.put(a, code); + final MvKey k = MvKey.code(a); + o.writes.put(k, code); + o.writeKeys.add(k); + } + + final Map updated = + (acct instanceof MutableAccount) ? ((MutableAccount) acct).getUpdatedStorage() : null; + if (updated != null && !updated.isEmpty()) { + final Map copy = new HashMap<>(updated); + o.storageByAddr.add(Map.entry(a, copy)); + for (final Map.Entry se : copy.entrySet()) { + final MvKey k = MvKey.storage(a, se.getKey()); + o.writes.put(k, se.getValue()); + o.writeKeys.add(k); + } + } + } + + for (final Address d : txUpdater.getDeletedAccountAddresses()) { + o.deleted.add(d); + // reflect clearing in MVMemory so higher transactions read cleared state + final MvKey bk = MvKey.balance(d); + final MvKey nk = MvKey.nonce(d); + final MvKey ck = MvKey.code(d); + o.writes.put(bk, Wei.ZERO); + o.writes.put(nk, Long.valueOf(0L)); + o.writes.put(ck, Bytes.EMPTY); + o.writeKeys.add(bk); + o.writeKeys.add(nk); + o.writeKeys.add(ck); + } + return o; + } + + /** + * Commit phase: called by the block processor in strict block order (location 0..n-1) on the real + * world state. Applies transaction {@code location}'s parallel outcome and returns its result, or + * empty to request sequential re-execution for this transaction. + */ + @Override + public Optional getProcessingResult( + final MutableWorldState worldState, + final Address miningBeneficiary, + final Transaction transaction, + final int location, + final Optional confirmedParallelizedTransactionCounter, + final Optional conflictingButCachedTransactionCounter) { + + if (!success || outcomes == null || location >= outcomes.length) { + return Optional.empty(); + } + final MvOutcome o = outcomes[location]; + if (o == null || o.result == null || !o.result.isSuccessful()) { + return Optional.empty(); + } + try { + final WorldUpdater updater = worldState.updater(); + // deletions win over writes for the same address + for (final Map.Entry e : o.balanceByAddr.entrySet()) { + if (!o.deleted.contains(e.getKey())) { + updater.getOrCreate(e.getKey()).setBalance(e.getValue()); + } + } + for (final Map.Entry e : o.nonceByAddr.entrySet()) { + if (!o.deleted.contains(e.getKey())) { + updater.getOrCreate(e.getKey()).setNonce(e.getValue()[0]); + } + } + for (final Map.Entry e : o.codeByAddr.entrySet()) { + if (!o.deleted.contains(e.getKey())) { + updater.getOrCreate(e.getKey()).setCode(e.getValue()); + } + } + for (final Map.Entry> e : o.storageByAddr) { + if (o.deleted.contains(e.getKey())) { + continue; + } + final MutableAccount ra = updater.getOrCreate(e.getKey()); + for (final Map.Entry se : e.getValue().entrySet()) { + ra.setStorageValue(se.getKey(), se.getValue()); + } + } + if (!o.beneficiaryDelta.isZero()) { + updater.getOrCreate(miningBeneficiary).incrementBalance(o.beneficiaryDelta); + } + for (final Address d : o.deleted) { + updater.deleteAccount(d); + } + updater.commit(); + committedParallel.incrementAndGet(); + confirmedParallelizedTransactionCounter.ifPresent(Counter::inc); + o.result.setIsProcessedInParallel(Optional.of(Boolean.TRUE)); + return Optional.of(o.result); + } catch (final RuntimeException ex) { + LOG.info("AERE Block-STM: commit of tx {} failed; sequential fallback", location, ex); + return Optional.empty(); + } + } + + @Override + protected org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelizedTransactionContext + runTransaction( + final ProtocolContext protocolContext, + final BlockHeader header, + final int transactionLocation, + final Transaction transaction, + final Address miningBeneficiary, + final BlockHashLookup blockHashLookupParam, + final Wei blobGasPriceParam, + final Optional blockAccessListBuilder, + final Optional maybeParentHeader) { + // Unused: AERE Block-STM overrides runAsyncBlock wholesale. + return null; + } + + /** @return whether the last parallel run verified (reached done with no hard failure). */ + public boolean lastRunSucceeded() { + return success; + } + + /** @return Block-STM statistics for the last run. */ + public BlockStmStats stats() { + return new BlockStmStats( + outcomes == null ? 0 : outcomes.length, + numThreads, + totalExecutions.get(), + totalValidations.get(), + totalAborts.get(), + committedParallel.get(), + success, + snapshotNanos, + execPhaseNanos); + } + + /** @return wall-clock nanos of the last parallel EXECUTION PHASE (worker pool only). */ + public long execPhaseNanos() { + return execPhaseNanos; + } + + /** @return wall-clock nanos spent building the last flat base pre-snapshot. */ + public long snapshotNanos() { + return snapshotNanos; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmPreprocessing.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmPreprocessing.java new file mode 100644 index 0000000000..db6562e254 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmPreprocessing.java @@ -0,0 +1,119 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.PreprocessingFunction; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList.BlockAccessListBuilder; +import org.hyperledger.besu.ethereum.mainnet.parallelization.PreprocessingContext; +import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.PathBasedWorldStateProvider; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; + +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Preprocessing hook that runs AERE Block-STM optimistic parallel execution over the block's + * transactions before the sequential commit loop. Occupies the same {@code PreprocessingFunction} + * slot Besu uses for its built-in single-round parallelizer, but drives the AERE multi-version + * Block-STM engine instead. A fresh {@link AereBlockStmEngine} is created per block, so no state + * leaks between blocks. + */ +@SuppressWarnings("MissingSummary") +public class AereBlockStmPreprocessing implements PreprocessingFunction { + + private static final Logger LOG = LoggerFactory.getLogger(AereBlockStmPreprocessing.class); + + private final MainnetTransactionProcessor transactionProcessor; + private final int numThreads; + + /** + * @param transactionProcessor the EVM transaction processor + * @param numThreads worker-pool size (typically NCPU) + */ + public AereBlockStmPreprocessing( + final MainnetTransactionProcessor transactionProcessor, final int numThreads) { + this.transactionProcessor = transactionProcessor; + this.numThreads = numThreads; + } + + @Override + public Optional run( + final ProtocolContext protocolContext, + final BlockHeader blockHeader, + final List transactions, + final Address miningBeneficiary, + final BlockHashLookup blockHashLookup, + final Wei blobGasPrice, + final Optional blockAccessListBuilder, + final Optional maybeBlockBal, + final Optional maybeParentHeader) { + + if (!(protocolContext.getWorldStateArchive() instanceof PathBasedWorldStateProvider)) { + // Block-STM multi-version view requires a Bonsai/path-based world state. + return Optional.empty(); + } + + final AereBlockStmEngine engine = new AereBlockStmEngine(transactionProcessor, numThreads); + engine.runAsyncBlock( + protocolContext, + blockHeader, + transactions, + miningBeneficiary, + blockHashLookup, + blobGasPrice, + Runnable::run, + blockAccessListBuilder, + maybeParentHeader); + + final BlockStmStats stats = engine.stats(); + LOG.debug( + "AERE Block-STM block #{}: txns={} threads={} exec={} valid={} aborts={} committedParallel={}" + + " verified={}", + blockHeader.getNumber(), + stats.numTxns(), + stats.numThreads(), + stats.totalExecutions(), + stats.totalValidations(), + stats.totalAborts(), + stats.committedParallel(), + stats.verified()); + // Parseable single-line marker for the benchmark harness: the isolated EXECUTION-PHASE nanos + // (worker pool only, excludes the flat-snapshot mint AND the downstream serial Bonsai trie + // commit) plus the snapshot-mint nanos, per block. Gated on AERE_BLOCKSTM_STATS=1 so it is + // silent in normal operation and only emitted during benchmarking. + if ("1".equals(System.getenv("AERE_BLOCKSTM_STATS")) && stats.numTxns() > 0) { + LOG.info( + "AERE-BLOCKSTM-STATS blk={} n={} thr={} execNs={} snapNs={} exec={} aborts={}" + + " committedParallel={} verified={}", + blockHeader.getNumber(), + stats.numTxns(), + stats.numThreads(), + stats.execPhaseNanos(), + stats.snapshotNanos(), + stats.totalExecutions(), + stats.totalAborts(), + stats.committedParallel(), + stats.verified()); + } + + return Optional.of(new PreprocessingContext(engine)); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmProcessor.java new file mode 100644 index 0000000000..db2065a4a6 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/AereBlockStmProcessor.java @@ -0,0 +1,160 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.BlockProcessingResult; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.PreprocessingFunction.NoPreprocessing; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.TransactionReceiptFactory; +import org.hyperledger.besu.ethereum.mainnet.BalConfiguration; +import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; +import org.hyperledger.besu.ethereum.mainnet.block.access.list.BlockAccessList; +import org.hyperledger.besu.ethereum.mainnet.parallelization.MainnetParallelBlockProcessor; +import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; +import org.hyperledger.besu.plugin.services.MetricsSystem; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * L1 block processor that executes a block's transactions with AERE Block-STM optimistic parallel + * execution, committing in block order to produce a state root bit-identical to sequential + * execution. It reuses Besu's parallel consume path ({@link MainnetParallelBlockProcessor}: the + * overridden {@code getTransactionProcessingResult} pulls per-transaction results from the + * preprocessing context) but swaps Besu's single-round parallelizer for the AERE multi-version + * Block-STM engine via {@link AereBlockStmPreprocessing}. + * + *

    Fork gate: this processor is selected only for the AERE {@code AerePQC} (FUTURE_EIPS) hard fork + * and only when {@code AERE_BLOCKSTM=1}, so a single activation delivers PQC precompiles + EIP-2935 + * + parallel execution together. On any parallel-phase failure the whole block falls back to + * sequential execution, so the processor can never persist an unverified state root. + */ +@SuppressWarnings("MissingSummary") +public class AereBlockStmProcessor extends MainnetParallelBlockProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(AereBlockStmProcessor.class); + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * @param transactionProcessor the EVM transaction processor + * @param transactionReceiptFactory receipt factory + * @param blockReward block reward + * @param miningBeneficiaryCalculator beneficiary calculator + * @param skipZeroBlockRewards whether to skip zero rewards + * @param protocolSchedule the protocol schedule + * @param balConfiguration BAL configuration + * @param metricsSystem metrics + */ + public AereBlockStmProcessor( + final MainnetTransactionProcessor transactionProcessor, + final TransactionReceiptFactory transactionReceiptFactory, + final Wei blockReward, + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final boolean skipZeroBlockRewards, + final ProtocolSchedule protocolSchedule, + final BalConfiguration balConfiguration, + final MetricsSystem metricsSystem) { + super( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule, + balConfiguration, + metricsSystem); + } + + @Override + public BlockProcessingResult processBlock( + final ProtocolContext protocolContext, + final Blockchain blockchain, + final MutableWorldState worldState, + final Block block) { + return processBlock(protocolContext, blockchain, worldState, block, Optional.empty()); + } + + @Override + public BlockProcessingResult processBlock( + final ProtocolContext protocolContext, + final Blockchain blockchain, + final MutableWorldState worldState, + final Block block, + final Optional blockAccessList) { + + final BlockProcessingResult result = + super.processBlock( + protocolContext, + blockchain, + worldState, + block, + blockAccessList, + new AereBlockStmPreprocessing(transactionProcessor, NCPU)); + + if (result.isFailed()) { + LOG.info( + "AERE Block-STM processing failure for block #{} ({}); falling back to sequential", + block.getHeader().getNumber(), + block.getHash()); + if (worldState instanceof BonsaiWorldState) { + ((BonsaiWorldStateUpdateAccumulator) worldState.updater()).reset(); + } + return super.processBlock( + protocolContext, blockchain, worldState, block, blockAccessList, new NoPreprocessing()); + } + return result; + } + + /** Builds {@link AereBlockStmProcessor}, mirroring Besu's parallel builder. */ + public static class AereBlockStmProcessorBuilder + implements ProtocolSpecBuilder.BlockProcessorBuilder { + + private final MetricsSystem metricsSystem; + + /** + * @param metricsSystem metrics + */ + public AereBlockStmProcessorBuilder(final MetricsSystem metricsSystem) { + this.metricsSystem = metricsSystem; + } + + @Override + public BlockProcessor apply( + final MainnetTransactionProcessor transactionProcessor, + final TransactionReceiptFactory transactionReceiptFactory, + final Wei blockReward, + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final boolean skipZeroBlockRewards, + final ProtocolSchedule protocolSchedule, + final BalConfiguration balConfiguration) { + return new AereBlockStmProcessor( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule, + balConfiguration, + metricsSystem); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BaseReader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BaseReader.java new file mode 100644 index 0000000000..2020655ef9 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BaseReader.java @@ -0,0 +1,67 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +/** + * Read-only access to the immutable base (parent-block) world state. A {@code Base} read in + * MVMemory falls through to here. Implementations must be safe for concurrent reads (the base state + * does not change during block execution). + */ +@SuppressWarnings("MissingSummary") +public interface BaseReader { + + /** + * Whether the account exists in base state. + * + * @param address the account + * @return true if present + */ + boolean accountExists(Address address); + + /** + * Base nonce (0 if absent). + * + * @param address the account + * @return nonce + */ + long getNonce(Address address); + + /** + * Base balance (ZERO if absent). + * + * @param address the account + * @return balance + */ + Wei getBalance(Address address); + + /** + * Base code (EMPTY if absent). + * + * @param address the account + * @return code + */ + Bytes getCode(Address address); + + /** + * Base storage slot value (ZERO if absent). + * + * @param address the account + * @param slot the slot + * @return value + */ + UInt256 getStorageValue(Address address, UInt256 slot); +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BlockStmStats.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BlockStmStats.java new file mode 100644 index 0000000000..a6a05f00ca --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/BlockStmStats.java @@ -0,0 +1,38 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +/** + * Aggregate statistics for one Block-STM run. + * + * @param numTxns transactions in the block + * @param numThreads worker-pool size + * @param totalExecutions total incarnations executed (excess over numTxns = re-executions) + * @param totalValidations total validation tasks run + * @param totalAborts validation failures that triggered re-execution + * @param committedParallel transactions committed from a parallel outcome (not sequential fallback) + * @param verified whether the run reached scheduler.done() with no hard failure + * @param snapshotNanos wall-clock nanos spent building the flat base pre-snapshot (single Bonsai + * view mint + wrapper), excluded from the parallel execution phase + * @param execPhaseNanos wall-clock nanos of the PARALLEL EXECUTION PHASE only (worker pool start to + * termination); excludes both the snapshot build and the downstream serial Bonsai trie commit + */ +@SuppressWarnings("MissingSummary") +public record BlockStmStats( + int numTxns, + int numThreads, + int totalExecutions, + int totalValidations, + int totalAborts, + int committedParallel, + boolean verified, + long snapshotNanos, + long execPhaseNanos) {} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/FlatBaseSnapshot.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/FlatBaseSnapshot.java new file mode 100644 index 0000000000..20c3441fca --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/FlatBaseSnapshot.java @@ -0,0 +1,165 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.trie.pathbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.evm.account.Account; + +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +/** + * A FLAT, immutable, in-memory pre-snapshot of the parent-block base world state, shared lock-free + * by every Block-STM worker. + * + *

    Iteration-1 gave each worker its OWN frozen {@link BonsaiWorldState} view and routed every base + * read through a layered-trie traversal. Two costs dominated cheap-tx blocks and capped scaling on + * execution-bound blocks (task bottleneck #2): (1) minting {@code numThreads} BonsaiWorldState + * snapshots per block, and (2) re-walking the parent trie on every read, on every worker, on every + * re-execution incarnation, even for keys already seen. + * + *

    This snapshot mints the parent Bonsai view exactly ONCE. Each distinct base value (account + * facet triple, or one storage slot) is materialised the first time any worker touches it and cached + * in a {@link ConcurrentHashMap}; every subsequent read by any worker/incarnation is a lock-free + * O(1) hashmap lookup with no trie traversal. Cold loads (cache misses) route through the single + * shared BonsaiWorldState and are serialised on {@link #loadLock} because a BonsaiWorldState is not + * safe for concurrent {@code get}; this cold path is one-time-per-key and negligible for + * execution-bound workloads, and the hot path is fully lock-free. + * + *

    Serial-equivalence is preserved: the base state is immutable for the whole parallel round, so + * every worker observes identical parent values regardless of load order (parallelRoot == + * sequentialRoot). An account is materialised as a whole {@link Shell} (existence + nonce + balance + * + code) in one trie hit, so the common pattern of reading several facets of the same account costs + * a single traversal. + */ +@SuppressWarnings("MissingSummary") +final class FlatBaseSnapshot implements BaseReader { + + /** Immutable materialised account facet triple. */ + private static final class Shell { + final boolean exists; + final long nonce; + final Wei balance; + final Bytes code; + + Shell(final boolean exists, final long nonce, final Wei balance, final Bytes code) { + this.exists = exists; + this.nonce = nonce; + this.balance = balance; + this.code = code; + } + } + + private static final Shell ABSENT = new Shell(false, 0L, Wei.ZERO, Bytes.EMPTY); + + /** Composite key for a materialised storage slot. */ + private static final class SlotKey { + private final Address address; + private final UInt256 slot; + private final int hash; + + SlotKey(final Address address, final UInt256 slot) { + this.address = address; + this.slot = slot; + this.hash = Objects.hash(address, slot); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SlotKey)) { + return false; + } + final SlotKey k = (SlotKey) o; + return address.equals(k.address) && slot.equals(k.slot); + } + + @Override + public int hashCode() { + return hash; + } + } + + private final BonsaiWorldState bws; + private final Object loadLock = new Object(); + private final ConcurrentHashMap accounts = new ConcurrentHashMap<>(); + private final ConcurrentHashMap storage = new ConcurrentHashMap<>(); + + FlatBaseSnapshot(final BonsaiWorldState bws) { + this.bws = bws; + } + + private Shell shell(final Address a) { + final Shell hit = accounts.get(a); // lock-free hot path + if (hit != null) { + return hit; + } + synchronized (loadLock) { + final Shell again = accounts.get(a); + if (again != null) { + return again; + } + final Account acct = bws.get(a); + final Shell shell = + acct == null + ? ABSENT + : new Shell(true, acct.getNonce(), acct.getBalance(), acct.getCode()); + accounts.put(a, shell); + return shell; + } + } + + @Override + public boolean accountExists(final Address address) { + return shell(address).exists; + } + + @Override + public long getNonce(final Address address) { + return shell(address).nonce; + } + + @Override + public Wei getBalance(final Address address) { + return shell(address).balance; + } + + @Override + public Bytes getCode(final Address address) { + return shell(address).code; + } + + @Override + public UInt256 getStorageValue(final Address address, final UInt256 slot) { + final SlotKey key = new SlotKey(address, slot); + final UInt256 hit = storage.get(key); // lock-free hot path + if (hit != null) { + return hit; + } + synchronized (loadLock) { + final UInt256 again = storage.get(key); + if (again != null) { + return again; + } + final Account acct = bws.get(address); + final UInt256 value = acct == null ? UInt256.ZERO : acct.getStorageValue(slot); + storage.put(key, value); + return value; + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvAccount.java new file mode 100644 index 0000000000..97feae1227 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvAccount.java @@ -0,0 +1,82 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.fluent.SimpleAccount; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; + +/** + * An account facade over multi-version memory for a single Block-STM incarnation. Reads of a storage + * slot are routed through MVMemory (highest lower-index write, else base), recording the observed + * provenance into the execution's read set. Writes buffer in the inherited {@link SimpleAccount} + * overlay and are extracted as the write set after execution. + */ +@SuppressWarnings("MissingSummary") +public class MvAccount extends SimpleAccount { + + private final MvContext ctx; + private final Address addr; + + // Values this incarnation started from (for write-set diffing). + private final long seededNonce; + private final Wei seededBalance; + private final Hash seededCodeHash; + + MvAccount( + final MvContext ctx, + final Address address, + final long nonce, + final Wei balance, + final Bytes code) { + super(null, address, nonce, balance, code); + this.ctx = ctx; + this.addr = address; + this.seededNonce = nonce; + this.seededBalance = balance; + this.seededCodeHash = code == null || code.isEmpty() ? Hash.EMPTY : Hash.hash(code); + } + + @Override + public UInt256 getOriginalStorageValue(final UInt256 key) { + final MvKey k = MvKey.storage(addr, key); + final MvMemory.ReadResult rr = ctx.mv.read(k, ctx.txnIdx); + if (rr.isBlocked()) { + if (ctx.blockedOn < 0) { + ctx.blockedOn = rr.blockingTxn(); + } + return UInt256.ZERO; + } + if (!rr.descriptor().isBase()) { + ctx.record(k, rr.descriptor()); + final Object v = rr.value(); + return v == null ? UInt256.ZERO : (UInt256) v; + } + ctx.record(k, MvMemory.ReadDescriptor.BASE); + return ctx.base.getStorageValue(addr, key); + } + + long seededNonce() { + return seededNonce; + } + + Wei seededBalance() { + return seededBalance; + } + + Hash seededCodeHash() { + return seededCodeHash; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvBaseLayer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvBaseLayer.java new file mode 100644 index 0000000000..bbb54d60c2 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvBaseLayer.java @@ -0,0 +1,149 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.fluent.SimpleWorld; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; + +/** + * The base (bottom) world layer for one Block-STM incarnation. It never holds writes: every EVM + * write lands in a child {@link SimpleWorld} layer obtained via {@link #updater()}. On a miss, this + * layer materialises an {@link MvAccount} by routing balance/nonce/code reads through MVMemory + * (highest lower-index write, else the immutable base state), recording provenance into the read + * set. Storage reads route lazily inside {@link MvAccount}. + */ +@SuppressWarnings("MissingSummary") +public class MvBaseLayer extends SimpleWorld { + + private final MvContext ctx; + private final Map materialized = new HashMap<>(); + private final Set

    absent = new HashSet<>(); + + MvBaseLayer(final MvContext ctx) { + super(); + this.ctx = ctx; + } + + @Override + public Account get(final Address address) { + return getAccount(address); + } + + @Override + public MutableAccount getAccount(final Address address) { + final MvAccount m = materialized.get(address); + if (m != null) { + return m; + } + if (absent.contains(address)) { + return null; + } + final MvAccount created = materialize(address); + if (created == null) { + absent.add(address); + return null; + } + materialized.put(address, created); + return created; + } + + /** + * The seeded (pre-execution) account, for write-set diffing. + * + * @param address the account + * @return the materialised account, or null if it was absent / freshly created in a child layer + */ + MvAccount materializedAccount(final Address address) { + return materialized.get(address); + } + + private MvAccount materialize(final Address a) { + boolean exists = ctx.base.accountExists(a); + + // balance (beneficiary balance is excluded from conflict tracking) + final Wei balance; + if (ctx.isBeneficiary(a)) { + balance = ctx.base.getBalance(a); + } else { + final MvKey k = MvKey.balance(a); + final MvMemory.ReadResult rr = ctx.mv.read(k, ctx.txnIdx); + if (rr.isBlocked()) { + block(rr); + balance = Wei.ZERO; + } else if (!rr.descriptor().isBase()) { + ctx.record(k, rr.descriptor()); + balance = (Wei) rr.value(); + exists = true; + } else { + ctx.record(k, MvMemory.ReadDescriptor.BASE); + balance = ctx.base.getBalance(a); + } + } + + // nonce + final long nonce; + { + final MvKey k = MvKey.nonce(a); + final MvMemory.ReadResult rr = ctx.mv.read(k, ctx.txnIdx); + if (rr.isBlocked()) { + block(rr); + nonce = 0L; + } else if (!rr.descriptor().isBase()) { + ctx.record(k, rr.descriptor()); + nonce = (Long) rr.value(); + exists = true; + } else { + ctx.record(k, MvMemory.ReadDescriptor.BASE); + nonce = ctx.base.getNonce(a); + } + } + + // code + final Bytes code; + { + final MvKey k = MvKey.code(a); + final MvMemory.ReadResult rr = ctx.mv.read(k, ctx.txnIdx); + if (rr.isBlocked()) { + block(rr); + code = Bytes.EMPTY; + } else if (!rr.descriptor().isBase()) { + ctx.record(k, rr.descriptor()); + code = (Bytes) rr.value(); + exists = true; + } else { + ctx.record(k, MvMemory.ReadDescriptor.BASE); + code = ctx.base.getCode(a); + } + } + + if (!exists) { + // Absent account: reads recorded so a later creating write is caught by validation. + return null; + } + return new MvAccount(ctx, a, nonce, balance, code); + } + + private void block(final MvMemory.ReadResult rr) { + if (ctx.blockedOn < 0) { + ctx.blockedOn = rr.blockingTxn(); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvContext.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvContext.java new file mode 100644 index 0000000000..6f1902b421 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvContext.java @@ -0,0 +1,55 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Per-execution capture state for one incarnation of one transaction. Confined to the single worker + * thread executing that incarnation, so it needs no synchronization internally. + * + *

    Records the read set (key -> observed provenance) for later validation, and the address of the + * mining beneficiary, whose balance is excluded from conflict detection (its reward is applied + * additively during in-order commit, mirroring Besu's {@code traceBeforeRewardTransaction} trick). + */ +@SuppressWarnings("MissingSummary") +public final class MvContext { + + final int txnIdx; + final MvMemory mv; + final BaseReader base; + final Address beneficiary; + + /** key -> first observed provenance (dedup on key, preserve encounter order). */ + final Map reads = new LinkedHashMap<>(); + + /** set to the blocking txn index (>=0) the first time a read hit an ESTIMATE. */ + int blockedOn = -1; + + MvContext( + final int txnIdx, final MvMemory mv, final BaseReader base, final Address beneficiary) { + this.txnIdx = txnIdx; + this.mv = mv; + this.base = base; + this.beneficiary = beneficiary; + } + + void record(final MvKey key, final MvMemory.ReadDescriptor desc) { + reads.putIfAbsent(key, desc); + } + + boolean isBeneficiary(final Address a) { + return beneficiary != null && beneficiary.equals(a); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvKey.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvKey.java new file mode 100644 index 0000000000..b06eecf35c --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvKey.java @@ -0,0 +1,156 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import org.hyperledger.besu.datatypes.Address; + +import java.util.Objects; + +import org.apache.tuweni.units.bigints.UInt256; + +/** + * A location in world state that Block-STM tracks for read/write-set conflict detection. Mirrors the + * Rust {@code Key} enum (Balance / Storage) generalised to the four EVM account facets that a + * transaction can read or write: BALANCE, NONCE, CODE and a STORAGE slot. + * + *

    This is the unit over which MVMemory shards and over which read-set validation compares + * provenance. Two {@code MvKey}s are equal iff they refer to the same facet of the same account (and + * same slot for STORAGE). + */ +@SuppressWarnings("MissingSummary") +public final class MvKey { + + /** The account facet this key addresses. */ + public enum Kind { + /** Account balance. */ + BALANCE, + /** Account nonce. */ + NONCE, + /** Account code. */ + CODE, + /** A single storage slot. */ + STORAGE + } + + private final Kind kind; + private final Address address; + private final UInt256 slot; // null unless STORAGE + private final int hash; + + private MvKey(final Kind kind, final Address address, final UInt256 slot) { + this.kind = kind; + this.address = address; + this.slot = slot; + this.hash = Objects.hash(kind, address, slot); + } + + /** + * Balance key. + * + * @param address the account + * @return the key + */ + public static MvKey balance(final Address address) { + return new MvKey(Kind.BALANCE, address, null); + } + + /** + * Nonce key. + * + * @param address the account + * @return the key + */ + public static MvKey nonce(final Address address) { + return new MvKey(Kind.NONCE, address, null); + } + + /** + * Code key. + * + * @param address the account + * @return the key + */ + public static MvKey code(final Address address) { + return new MvKey(Kind.CODE, address, null); + } + + /** + * Storage-slot key. + * + * @param address the account + * @param slot the slot + * @return the key + */ + public static MvKey storage(final Address address, final UInt256 slot) { + return new MvKey(Kind.STORAGE, address, slot); + } + + /** + * The facet kind. + * + * @return kind + */ + public Kind kind() { + return kind; + } + + /** + * The account address. + * + * @return address + */ + public Address address() { + return address; + } + + /** + * Deterministic shard index, mirroring the Rust FNV-1a spread. + * + * @param shards number of shards + * @return shard index in [0, shards) + */ + public int shard(final int shards) { + long h = 0xcbf29ce484222325L; + h = fnv(h, kind.ordinal() + 1L); + h = fnv(h, address.hashCode() & 0xffffffffL); + h = fnv(h, slot == null ? 0L : slot.hashCode() & 0xffffffffL); + int s = (int) Long.remainderUnsigned(h, shards); + return s; + } + + private static long fnv(final long acc, final long b) { + long h = acc ^ b; + h *= 0x100000001b3L; + return h; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof MvKey)) { + return false; + } + final MvKey k = (MvKey) o; + return kind == k.kind && address.equals(k.address) && Objects.equals(slot, k.slot); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return kind + "(" + address + (slot == null ? "" : "," + slot.toShortHexString()) + ")"; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvMemory.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvMemory.java new file mode 100644 index 0000000000..914df3a3ad --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/MvMemory.java @@ -0,0 +1,266 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Multi-version shared memory (MVMemory). Faithful Java port of the AERE Rust {@code mvmemory.rs} + * (Gelashvili et al. 2022). + * + *

    For every {@link MvKey}, MVMemory keeps an ordered map {@code txnIndex -> Cell}, where a cell + * is either a concrete value written by that transaction's latest incarnation, or an ESTIMATE + * placeholder (a marker that a lower transaction previously wrote here but is currently being + * re-executed, so its value is not trustworthy). + * + *

    A read by transaction {@code i} for key {@code k} returns the entry of the HIGHEST transaction + * {@code j < i} that wrote {@code k}. That is what makes speculative parallel execution + * serial-equivalent: transaction {@code i} always observes the writes of all lower-indexed + * transactions that have run, and nothing from higher ones. + * + *

    Storage is sharded across many independent locks keyed by a hash of the key, so + * non-conflicting transactions touch disjoint locks and run without contention. + */ +@SuppressWarnings("MissingSummary") +public class MvMemory { + + private static final int SHARDS = 256; + + /** What a read observed, recorded into the reader's read set for later validation. */ + public enum DescKind { + /** Nothing below the reader wrote this key: it came from base state. */ + BASE, + /** It was written by some (txnIndex, incarnation). */ + VERSION + } + + /** A read-set descriptor: BASE, or VERSION(txnIndex, incarnation). */ + public static final class ReadDescriptor { + /** Shared BASE singleton. */ + public static final ReadDescriptor BASE = new ReadDescriptor(DescKind.BASE, -1, -1); + + private final DescKind kind; + private final int txnIndex; + private final int incarnation; + + private ReadDescriptor(final DescKind kind, final int txnIndex, final int incarnation) { + this.kind = kind; + this.txnIndex = txnIndex; + this.incarnation = incarnation; + } + + static ReadDescriptor version(final int txnIndex, final int incarnation) { + return new ReadDescriptor(DescKind.VERSION, txnIndex, incarnation); + } + + /** @return true if this descriptor is a BASE read. */ + public boolean isBase() { + return kind == DescKind.BASE; + } + + boolean sameProvenance(final ReadDescriptor other) { + if (kind != other.kind) { + return false; + } + if (kind == DescKind.BASE) { + return true; + } + return txnIndex == other.txnIndex && incarnation == other.incarnation; + } + } + + /** Live read outcome: OK(descriptor,value) or BLOCKED(blockingTxn). */ + public static final class ReadResult { + final boolean blocked; + final int blockingTxn; + final ReadDescriptor descriptor; + final Object value; + + private ReadResult( + final boolean blocked, + final int blockingTxn, + final ReadDescriptor descriptor, + final Object value) { + this.blocked = blocked; + this.blockingTxn = blockingTxn; + this.descriptor = descriptor; + this.value = value; + } + + static ReadResult ok(final ReadDescriptor d, final Object v) { + return new ReadResult(false, -1, d, v); + } + + static ReadResult blocked(final int blockingTxn) { + return new ReadResult(true, blockingTxn, null, null); + } + + /** @return true if this read hit an ESTIMATE and the reader must park. */ + public boolean isBlocked() { + return blocked; + } + + /** @return the blocking txn index (valid iff {@link #isBlocked()}). */ + public int blockingTxn() { + return blockingTxn; + } + + /** @return the observed provenance descriptor (valid iff not blocked). */ + public ReadDescriptor descriptor() { + return descriptor; + } + + /** @return the observed value, or null for a BASE read (caller falls back to base state). */ + public Object value() { + return value; + } + } + + private static final class Cell { + final boolean estimate; + final int incarnation; + final Object value; + + private Cell(final boolean estimate, final int incarnation, final Object value) { + this.estimate = estimate; + this.incarnation = incarnation; + this.value = value; + } + + static Cell value(final int inc, final Object v) { + return new Cell(false, inc, v); + } + + static final Cell ESTIMATE = new Cell(true, -1, null); + } + + private final ReentrantLock[] locks = new ReentrantLock[SHARDS]; + // shard -> (key -> (txnIndex -> Cell)) + private final Map>[] shards; + + /** New empty MVMemory. */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public MvMemory() { + shards = new Map[SHARDS]; + for (int i = 0; i < SHARDS; i++) { + locks[i] = new ReentrantLock(); + shards[i] = new HashMap<>(); + } + } + + /** + * Read the value visible to {@code txnIdx}: the highest entry strictly below it. + * + * @param key the key + * @param txnIdx the reading transaction index + * @return the read result + */ + public ReadResult read(final MvKey key, final int txnIdx) { + final int s = key.shard(SHARDS); + locks[s].lock(); + try { + final TreeMap versions = shards[s].get(key); + if (versions != null) { + final Map.Entry e = versions.lowerEntry(txnIdx); + if (e != null) { + final Cell cell = e.getValue(); + if (cell.estimate) { + return ReadResult.blocked(e.getKey()); + } + return ReadResult.ok(ReadDescriptor.version(e.getKey(), cell.incarnation), cell.value); + } + } + return ReadResult.ok(ReadDescriptor.BASE, null); + } finally { + locks[s].unlock(); + } + } + + /** + * Install {@code txnIdx}'s new write set (incarnation {@code inc}); remove stale entries the + * previous incarnation wrote but this one no longer does. Returns true if the written key-SET + * changed (a key added or removed) — Block-STM's "wrote a new path", which triggers revalidation + * of the suffix. + * + * @param txnIdx the writing transaction + * @param inc the incarnation + * @param writes the new write set + * @param prevKeys the keys written by the previous incarnation + * @return whether the written key-set changed + */ + public boolean applyWriteSet( + final int txnIdx, + final int inc, + final Map writes, + final Set prevKeys) { + for (final Map.Entry w : writes.entrySet()) { + final MvKey k = w.getKey(); + final int s = k.shard(SHARDS); + locks[s].lock(); + try { + shards[s].computeIfAbsent(k, kk -> new TreeMap<>()).put(txnIdx, Cell.value(inc, w.getValue())); + } finally { + locks[s].unlock(); + } + } + boolean removedAny = false; + for (final MvKey k : prevKeys) { + if (!writes.containsKey(k)) { + final int s = k.shard(SHARDS); + locks[s].lock(); + try { + final TreeMap versions = shards[s].get(k); + if (versions != null) { + versions.remove(txnIdx); + } + } finally { + locks[s].unlock(); + } + removedAny = true; + } + } + boolean addedAny = false; + for (final MvKey k : writes.keySet()) { + if (!prevKeys.contains(k)) { + addedAny = true; + break; + } + } + return addedAny || removedAny; + } + + /** + * Convert all of {@code txnIdx}'s current writes (over {@code keys}) into ESTIMATE placeholders, + * so any concurrent higher transaction that reads one of these keys blocks on {@code txnIdx} + * instead of consuming a soon-to-be-wrong value. + * + * @param txnIdx the aborted transaction + * @param keys its written keys + */ + public void markEstimates(final int txnIdx, final Set keys) { + for (final MvKey k : keys) { + final int s = k.shard(SHARDS); + locks[s].lock(); + try { + final TreeMap versions = shards[s].get(k); + if (versions != null && versions.containsKey(txnIdx)) { + versions.put(txnIdx, Cell.ESTIMATE); + } + } finally { + locks[s].unlock(); + } + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/Scheduler.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/Scheduler.java new file mode 100644 index 0000000000..a967bbd6f3 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockstm/Scheduler.java @@ -0,0 +1,390 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.blockstm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +/** + * The Block-STM collaborative scheduler (Gelashvili et al. 2022, Algorithm 3). Faithful Java port of + * the AERE Rust {@code scheduler.rs}. + * + *

    Hands out EXECUTION and VALIDATION tasks to a worker pool. Two cursors ({@code executionIdx}, + * {@code validationIdx}) drive it; validation of a lower index always takes priority over execution + * of a higher one. When a transaction aborts or writes a new path, the relevant cursor is decreased + * so the affected range is revisited. The block is done when both cursors have passed the end AND no + * task is in flight AND no decrease happened during the check. Dependencies are handled without + * condvars via parking on the blocker's dependency list. + */ +@SuppressWarnings("MissingSummary") +public class Scheduler { + + /** Task type handed to a worker. */ + public enum TaskKind { + /** Run incarnation {@code inc} of transaction {@code idx}. */ + EXECUTION, + /** Re-check {@code idx}'s read set. */ + VALIDATION, + /** No task available right now; retry. */ + NO_TASK, + /** Block fully executed & validated. */ + DONE + } + + /** A unit of work, or a control signal. */ + public static final class Task { + /** Shared NO_TASK singleton. */ + public static final Task NO_TASK = new Task(TaskKind.NO_TASK, -1, -1); + /** Shared DONE singleton. */ + public static final Task DONE = new Task(TaskKind.DONE, -1, -1); + + final TaskKind kind; + final int idx; + final int inc; + + Task(final TaskKind kind, final int idx, final int inc) { + this.kind = kind; + this.idx = idx; + this.inc = inc; + } + + /** @return the task kind */ + public TaskKind kind() { + return kind; + } + + /** @return the transaction index */ + public int idx() { + return idx; + } + + /** @return the incarnation */ + public int inc() { + return inc; + } + } + + private enum StatusKind { + READY_TO_EXECUTE, + EXECUTING, + SUSPENDED, + EXECUTED, + ABORTING + } + + private static final class Status { + StatusKind kind; + int inc; + + Status(final StatusKind kind, final int inc) { + this.kind = kind; + this.inc = inc; + } + } + + private final int numTxns; + private final AtomicInteger executionIdx = new AtomicInteger(0); + private final AtomicInteger validationIdx = new AtomicInteger(0); + private final AtomicInteger decreaseCnt = new AtomicInteger(0); + private final AtomicInteger numActiveTasks = new AtomicInteger(0); + private final AtomicBoolean done = new AtomicBoolean(false); + private final Status[] status; + private final ReentrantLock[] statusLocks; + private final List[] dependencies; + private final ReentrantLock[] depLocks; + + /** + * New scheduler over {@code numTxns} transactions. + * + * @param numTxns transaction count + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public Scheduler(final int numTxns) { + this.numTxns = numTxns; + this.status = new Status[numTxns]; + this.statusLocks = new ReentrantLock[numTxns]; + this.dependencies = new List[numTxns]; + this.depLocks = new ReentrantLock[numTxns]; + for (int i = 0; i < numTxns; i++) { + status[i] = new Status(StatusKind.READY_TO_EXECUTE, 0); + statusLocks[i] = new ReentrantLock(); + dependencies[i] = new ArrayList<>(); + depLocks[i] = new ReentrantLock(); + } + } + + /** @return true once the block is fully executed & validated. */ + public boolean done() { + return done.get(); + } + + private void decreaseExecutionIdx(final int target) { + executionIdx.getAndUpdate(cur -> Math.min(cur, target)); + decreaseCnt.incrementAndGet(); + } + + private void decreaseValidationIdx(final int target) { + validationIdx.getAndUpdate(cur -> Math.min(cur, target)); + decreaseCnt.incrementAndGet(); + } + + private void checkDone() { + final int observed = decreaseCnt.get(); + if (executionIdx.get() >= numTxns + && validationIdx.get() >= numTxns + && numActiveTasks.get() == 0 + && decreaseCnt.get() == observed) { + done.set(true); + } + } + + private Task tryIncarnate(final int idx) { + if (idx < numTxns) { + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.READY_TO_EXECUTE) { + final int inc = status[idx].inc; + status[idx].kind = StatusKind.EXECUTING; + return new Task(TaskKind.EXECUTION, idx, inc); + } + } finally { + statusLocks[idx].unlock(); + } + } + numActiveTasks.decrementAndGet(); + return null; + } + + private Task nextVersionToExecute() { + if (executionIdx.get() >= numTxns) { + checkDone(); + return null; + } + numActiveTasks.incrementAndGet(); + final int idx = executionIdx.getAndIncrement(); + return tryIncarnate(idx); + } + + private Task nextVersionToValidate() { + if (validationIdx.get() >= numTxns) { + checkDone(); + return null; + } + numActiveTasks.incrementAndGet(); + final int idx = validationIdx.getAndIncrement(); + if (idx < numTxns) { + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.EXECUTED) { + return new Task(TaskKind.VALIDATION, idx, status[idx].inc); + } + } finally { + statusLocks[idx].unlock(); + } + } + numActiveTasks.decrementAndGet(); + return null; + } + + /** + * Main entry point workers spin on. + * + * @return a task, NO_TASK (retry), or DONE + */ + public Task nextTask() { + int idle = 0; + while (true) { + if (done.get()) { + return Task.DONE; + } + final int v = validationIdx.get(); + final int e = executionIdx.get(); + Task task = (v < e) ? nextVersionToValidate() : nextVersionToExecute(); + if (task == null && v < e) { + // The preferred lower-index validation had nothing runnable (its target is still + // EXECUTING on another worker). Rather than busy-draining validationIdx while pending + // independent EXECUTION tasks sit ungrabbed (which starved real parallel work and made + // the execution phase anti-scale with worker count), try to grab a pending execution. + // This is scheduling-order only: executing any READY incarnation in any order is already + // permitted by Block-STM, and read-set validation still re-checks and re-executes on any + // conflict, so the committed result stays bit-identical to sequential. + task = nextVersionToExecute(); + } + if (task != null) { + return task; + } + if (done.get()) { + return Task.DONE; + } + // Nothing runnable right now: back off so idle workers stop ping-ponging the shared + // cursors/locks across cores (that cache-line contention slows the workers doing real + // execution). Pure CPU hint / brief park; does not alter task selection or ordering. + if (++idle < 128) { + Thread.onSpinWait(); + } else { + java.util.concurrent.locks.LockSupport.parkNanos(Math.min((idle - 127) * 512L, 50_000L)); + } + } + } + + /** + * Register that {@code idx} depends on {@code blocking} (it read {@code blocking}'s ESTIMATE). + * + * @param idx the reader + * @param blocking the blocker + * @return true if {@code idx} was parked (caller drops the task); false if {@code blocking} + * already finished (caller re-executes {@code idx}) + */ + public boolean addDependency(final int idx, final int blocking) { + statusLocks[blocking].lock(); + try { + if (status[blocking].kind == StatusKind.EXECUTED) { + return false; + } + depLocks[blocking].lock(); + try { + dependencies[blocking].add(idx); + } finally { + depLocks[blocking].unlock(); + } + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.EXECUTING) { + status[idx].kind = StatusKind.SUSPENDED; + } + } finally { + statusLocks[idx].unlock(); + } + } finally { + statusLocks[blocking].unlock(); + } + numActiveTasks.decrementAndGet(); + return true; + } + + private void resume(final int idx) { + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.SUSPENDED) { + status[idx].kind = StatusKind.READY_TO_EXECUTE; + } + } finally { + statusLocks[idx].unlock(); + } + } + + private void setReadyAfterAbort(final int idx) { + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.ABORTING) { + status[idx].kind = StatusKind.READY_TO_EXECUTE; + status[idx].inc = status[idx].inc + 1; + } + } finally { + statusLocks[idx].unlock(); + } + } + + private void setExecuted(final int idx, final int inc) { + statusLocks[idx].lock(); + try { + status[idx].kind = StatusKind.EXECUTED; + status[idx].inc = inc; + } finally { + statusLocks[idx].unlock(); + } + } + + /** + * Called after an execution task completes. + * + * @param idx transaction + * @param inc incarnation + * @param wroteNewPath whether the written key-set changed vs the previous incarnation + * @return a follow-up task (inheriting the active-task unit) or NO_TASK (releasing it) + */ + public Task finishExecution(final int idx, final int inc, final boolean wroteNewPath) { + setExecuted(idx, inc); + + final List deps; + depLocks[idx].lock(); + try { + deps = new ArrayList<>(dependencies[idx]); + dependencies[idx].clear(); + } finally { + depLocks[idx].unlock(); + } + if (!deps.isEmpty()) { + final int minDep = Collections.min(deps); + for (final int d : deps) { + resume(d); + } + decreaseExecutionIdx(minDep); + } + + if (validationIdx.get() > idx) { + if (wroteNewPath) { + decreaseValidationIdx(idx); + numActiveTasks.decrementAndGet(); + return Task.NO_TASK; + } else { + return new Task(TaskKind.VALIDATION, idx, inc); + } + } else { + numActiveTasks.decrementAndGet(); + return Task.NO_TASK; + } + } + + /** + * Attempt to win the right to abort {@code idx} at {@code inc}. + * + * @param idx transaction + * @param inc incarnation + * @return true iff this caller flipped EXECUTED(inc) -> ABORTING(inc) + */ + public boolean tryValidationAbort(final int idx, final int inc) { + statusLocks[idx].lock(); + try { + if (status[idx].kind == StatusKind.EXECUTED && status[idx].inc == inc) { + status[idx].kind = StatusKind.ABORTING; + return true; + } + return false; + } finally { + statusLocks[idx].unlock(); + } + } + + /** + * Called after a validation task. + * + * @param idx transaction + * @param aborted whether validation failed AND this caller won {@link #tryValidationAbort} + * @return a follow-up execution task (inheriting the active-task unit) or NO_TASK (releasing it) + */ + public Task finishValidation(final int idx, final boolean aborted) { + if (aborted) { + setReadyAfterAbort(idx); + decreaseValidationIdx(idx + 1); + if (executionIdx.get() > idx) { + final Task t = tryIncarnate(idx); + return t == null ? Task.NO_TASK : t; + } + } + numActiveTasks.decrementAndGet(); + return Task.NO_TASK; + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD432GasCapUnsignedTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD432GasCapUnsignedTest.java new file mode 100644 index 0000000000..7dcf917525 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD432GasCapUnsignedTest.java @@ -0,0 +1,103 @@ +/* + * Copyright contributors to Besu. + * + * 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 + * + * Aere Network notice (Apache-2.0 section 4(b)): this file was added by Aere Network, 2026-09-28 (D-432). + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BlobType; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.GasLimitCalculator; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; +import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; +import org.hyperledger.besu.evm.gascalculator.OsakaGasCalculator; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +/** + * AERE D-432: the EIP-7825 transaction gas cap is compared UNSIGNED. The decoder reads gasLimit with readLongScalar, so a limit + * of 2^63 or more is a negative long here; a signed compare let it through the cap, while the second client refuses it at + * decoding. Found by the AIP-20 differential fuzzer (aips/aip20-conformitate/fuzz-pqtx). + */ +class AereD432GasCapUnsignedTest { + + private static final long CAP = 1L << 24; + private static final BigInteger CHAIN = BigInteger.valueOf(28001); + private static final KeyPair KEYS = SignatureAlgorithmFactory.getInstance().generateKeyPair(); + + private static MainnetTransactionValidator validator() { + final GasLimitCalculator glc = + new GasLimitCalculator() { + @Override + public long nextGasLimit(final long currentGasLimit, final long targetGasLimit, final long newBlockNumber) { + return currentGasLimit; + } + + @Override + public long transactionGasLimitCap() { + return CAP; + } + }; + return new MainnetTransactionValidator( + new OsakaGasCalculator(), glc, FeeMarket.london(0L), true, Optional.of(CHAIN), Set.of(TransactionType.EIP1559), + Set.of(BlobType.KZG_CELL_PROOFS), 2 * 24576); + } + + private static ValidationResult judge(final long gasLimit) { + final Transaction tx = + Transaction.builder() + .type(TransactionType.EIP1559) + .chainId(CHAIN) + .nonce(0) + .maxPriorityFeePerGas(Wei.ONE) + .maxFeePerGas(Wei.of(2_000_000_000L)) + .gasLimit(gasLimit) + .to(Address.ZERO) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .signAndBuild(KEYS); + return validator().validate(tx, Optional.of(Wei.of(1_000_000_000L)), Optional.empty(), TransactionValidationParams.processingBlock()); + } + + @Test + void aGasLimitOf2To63OrMoreIsRefusedByTheCapNotLetThroughAsANegativeLong() { + // 2^63, 2^63 + 5 and 2^64 - 1, as readLongScalar hands them over + for (final long g : new long[] {Long.MIN_VALUE, Long.MIN_VALUE + 5, -1L}) { + final ValidationResult r = judge(g); + assertThat(r.isValid()).withFailMessage("gasLimit %s admitted", Long.toUnsignedString(g)).isFalse(); + assertThat(r.getInvalidReason()) + .withFailMessage("gasLimit %s refused for %s, not the cap", Long.toUnsignedString(g), r.getInvalidReason()) + .isEqualTo(TransactionInvalidReason.EXCEEDS_TRANSACTION_GAS_LIMIT); + } + } + + @Test + void theCapItselfIsAdmittedAndOneAboveIsRefused() { + // positive control: the rule is armed at exactly 2^24 and the fixture is otherwise valid + assertThat(judge(CAP).isValid()).withFailMessage(() -> "gasLimit = cap refused: " + judge(CAP).getErrorMessage()).isTrue(); + assertThat(judge(CAP + 1).getInvalidReason()).isEqualTo(TransactionInvalidReason.EXCEEDS_TRANSACTION_GAS_LIMIT); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD433BftWithdrawalsTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD433BftWithdrawalsTest.java new file mode 100644 index 0000000000..6c935b7394 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AereD433BftWithdrawalsTest.java @@ -0,0 +1,75 @@ +/* + * Copyright contributors to Besu. + * + * 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 + * + * Aere Network notice (Apache-2.0 section 4(b)): this file was added by Aere Network, 2026-09-28 (D-433), as the regression + * test of the ported upstream fixes hyperledger/besu ffe8104ee2 (GHSA-p4h2-gvh4-pv6j) and b7f1387cb4. + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.GWei; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Withdrawal; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.units.bigints.UInt64; +import org.junit.jupiter.api.Test; + +/** + * AERE D-433: on a BFT chain (QBFT on 2800 and 28001, Shanghai active from genesis) withdrawals are not applicable, and the + * BFT schedule installs NotApplicableWithdrawals while keeping Shanghai's WithdrawalsProcessor. Before the port the validator + * returned true for anything, so a single Byzantine proposer could put withdrawals in a block and every honest Besu node would + * credit them as native balance. The port (upstream ffe8104ee2) refuses a non-empty list and a withdrawals root other than the + * empty trie root. + */ +class AereD433BftWithdrawalsTest { + + private static final WithdrawalsValidator V = new WithdrawalsValidator.NotApplicableWithdrawals(); + + @Test + void aByzantineWithdrawalListAndItsRootAreRefused() { + final List injected = + List.of(new Withdrawal(UInt64.valueOf(1), UInt64.valueOf(2), Address.fromHexString("0x000000000000000000000000000000000000c0de"), GWei.of(11))); + assertThat(V.validateWithdrawals(Optional.of(injected))).withFailMessage("a non-empty withdrawal list was accepted").isFalse(); + final BlockDataGenerator gen = new BlockDataGenerator(); + final Block withRoot = + gen.block(BlockDataGenerator.BlockOptions.create().setWithdrawals(Optional.of(injected)).setWithdrawalsRoot(Hash.ZERO)); + assertThat(V.validateWithdrawalsRoot(withRoot)).withFailMessage("a withdrawals root other than the empty trie was accepted").isFalse(); + // upstream b7f1387cb4: a root in the header with NO withdrawals field in the body is malformed, even when the root is the empty one + final Block noBody = gen.block(BlockDataGenerator.BlockOptions.create().setWithdrawals(Optional.empty()).setWithdrawalsRoot(Hash.EMPTY_TRIE_HASH)); + assertThat(V.validateWithdrawalsRoot(noBody)).withFailMessage("a header root without body withdrawals was accepted").isFalse(); + } + + @Test + void theEmptyFormsAreStillAccepted() { + // positive control: the blocks the chain actually carries (empty list, empty-trie root) and pre-Shanghai (absent) + assertThat(V.validateWithdrawals(Optional.of(Collections.emptyList()))).isTrue(); + assertThat(V.validateWithdrawals(Optional.empty())).isTrue(); + final BlockDataGenerator gen = new BlockDataGenerator(); + assertThat( + V.validateWithdrawalsRoot( + gen.block( + BlockDataGenerator.BlockOptions.create() + .setWithdrawals(Optional.of(Collections.emptyList())) + .setWithdrawalsRoot(Hash.EMPTY_TRIE_HASH)))) + .isTrue(); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarketAereFloorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarketAereFloorTest.java new file mode 100644 index 0000000000..b36bfd7811 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/feemarket/LondonFeeMarketAereFloorTest.java @@ -0,0 +1,129 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.ethereum.mainnet.feemarket; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +/** + * Tests for the AERE fork-gated base-fee floor in {@link LondonFeeMarket}. + * + *

    Proves: (a) fork UNSET is byte-behaviour-identical to upstream London (empty blocks decay to / + * stay at the legacy 7-wei floor, no 1-Gwei clamp); (b) once the fork block N is set with a 1-Gwei + * floor, empty blocks at/after N floor at EXACTLY 1 Gwei and never below; (c) the jump to >= 1 Gwei + * happens exactly at block N (block N-1 is still legacy); (d) an over-target block still rises above + * 1 Gwei via the normal EIP-1559 formula. + */ +public class LondonFeeMarketAereFloorTest { + + private static final long ONE_GWEI = 1_000_000_000L; + private static final long GAS_TARGET = 15_000_000L; + private static final long FORK_BLOCK = 100L; + + /** London fee market with an explicit AERE floor-fork configuration (bypasses global JVM config). */ + private static LondonFeeMarket market(final long aereForkBlock, final long aereFloorValueWei) { + return new LondonFeeMarket( + TransactionPriceCalculator.eip1559(), + 0L, // londonForkBlockNumber (kept off the tested block range) + Optional.empty(), + aereForkBlock, + aereFloorValueWei); + } + + // (a) UNSET fork => byte-identical to upstream: empty blocks decay to and stay at 7 wei, and no + // value is ever clamped up to 1 Gwei. + @Test + public void forkUnset_emptyBlocksDecayToSevenWei_noClampToOneGwei() { + final LondonFeeMarket unset = market(Long.MAX_VALUE, ONE_GWEI); + + // Decay many empty blocks from well above the floor: must settle at EXACTLY 7 wei. + Wei baseFee = Wei.of(10_000L); + for (int i = 0; i < 400; i++) { + baseFee = unset.computeBaseFee(500L + i, baseFee, 0L, GAS_TARGET); + } + assertThat(baseFee).isEqualTo(Wei.of(7L)); + + // Once at 7 wei it stays at 7 wei (never below), never jumps to 1 Gwei. + assertThat(unset.computeBaseFee(9_000L, Wei.of(7L), 0L, GAS_TARGET)).isEqualTo(Wei.of(7L)); + + // A mid-range empty-block value is NOT clamped up (proves the fork is a genuine no-op): + // 1000 - 1000/8 = 1000 - 125 = 875 wei. + assertThat(unset.computeBaseFee(9_000L, Wei.of(1_000L), 0L, GAS_TARGET)) + .isEqualTo(Wei.of(875L)); + } + + // (b) Fork ACTIVE => empty blocks at/after N floor at EXACTLY 1 Gwei and never drop below. + @Test + public void forkActive_emptyBlocksFloorAtExactlyOneGwei_neverBelow() { + final LondonFeeMarket forked = market(FORK_BLOCK, ONE_GWEI); + + // Sanity: raw EIP-1559 decrease from 1 Gwei on an empty block would be 875M; clamp pins it. + assertThat(forked.computeBaseFee(FORK_BLOCK + 1, Wei.of(ONE_GWEI), 0L, GAS_TARGET)) + .isEqualTo(Wei.of(ONE_GWEI)); + + // Many consecutive empty blocks after the fork: stays EXACTLY at 1 Gwei each block. + Wei baseFee = Wei.of(ONE_GWEI); + for (int i = 0; i < 50; i++) { + baseFee = forked.computeBaseFee(FORK_BLOCK + 1 + i, baseFee, 0L, GAS_TARGET); + assertThat(baseFee).isEqualTo(Wei.of(ONE_GWEI)); + } + + // Even a pathologically low parent baseFee (7 wei) is clamped up to exactly 1 Gwei post-fork. + assertThat(forked.computeBaseFee(FORK_BLOCK + 500, Wei.of(7L), 0L, GAS_TARGET)) + .isEqualTo(Wei.of(ONE_GWEI)); + } + + // (c) The jump to >= 1 Gwei happens exactly at the fork block N (N-1 is still legacy). + @Test + public void forkActivation_jumpHappensExactlyAtForkBlock() { + final LondonFeeMarket forked = market(FORK_BLOCK, ONE_GWEI); + + // Block N-1 is pre-fork: legacy behaviour, a 7-wei empty-block parent stays at 7 wei. + assertThat(forked.computeBaseFee(FORK_BLOCK - 1, Wei.of(7L), 0L, GAS_TARGET)) + .isEqualTo(Wei.of(7L)); + + // Block N itself jumps to >= 1 Gwei even though the parent baseFee was only 7 wei. + final Wei atForkBlock = forked.computeBaseFee(FORK_BLOCK, Wei.of(7L), 0L, GAS_TARGET); + assertThat(atForkBlock).isEqualTo(Wei.of(ONE_GWEI)); + assertThat(atForkBlock.greaterOrEqualThan(Wei.of(ONE_GWEI))).isTrue(); + } + + // (d) An over-target (active) block still rises above 1 Gwei via the normal EIP-1559 formula, + // and the clamp does not distort above-floor values (identical to the unset market's raw result). + @Test + public void forkActive_overTargetBlockRisesAboveOneGweiNormally() { + final LondonFeeMarket forked = market(FORK_BLOCK, ONE_GWEI); + final LondonFeeMarket unset = market(Long.MAX_VALUE, ONE_GWEI); + + final long parentGasUsed = 2 * GAS_TARGET; // over target => baseFee increases + final Wei parent = Wei.of(ONE_GWEI); + + // EIP-1559 rise: 1Gwei + 1Gwei * (2T - T)/T / 8 = 1Gwei + 1Gwei/8 = 1_125_000_000 wei. + final Wei rose = forked.computeBaseFee(FORK_BLOCK + 5, parent, parentGasUsed, GAS_TARGET); + assertThat(rose).isEqualTo(Wei.of(1_125_000_000L)); + assertThat(rose.greaterOrEqualThan(Wei.of(ONE_GWEI))).isTrue(); + assertThat(rose).isNotEqualTo(Wei.of(ONE_GWEI)); // strictly above the floor + + // Above-floor values are unaffected by the clamp: identical to the unset (no-op) market. + assertThat(rose) + .isEqualTo(unset.computeBaseFee(FORK_BLOCK + 5, parent, parentGasUsed, GAS_TARGET)); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmBenchmarkTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmBenchmarkTest.java new file mode 100644 index 0000000000..aac192aeba --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmBenchmarkTest.java @@ -0,0 +1,620 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.MINING_BENEFICIARY; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.hyperledger.besu.config.GenesisConfig; +import org.hyperledger.besu.config.JsonUtil; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.BlockProcessingResult; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.Util; +import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.blockstm.AereBlockStmEngine; +import org.hyperledger.besu.ethereum.mainnet.blockstm.AereBlockStmPreprocessing; +import org.hyperledger.besu.ethereum.mainnet.blockstm.BlockStmStats; +import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.WorldStateQueryParams; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; + +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * AERE Block-STM benchmark + verification harness (chain-independent, block-processor grain). + * + *

    Proves serial-equivalence (parallel state root == sequential state root) for an independent + * workload AND a genuine read/write-conflict workload (all transfers to one shared recipient, which + * forces the multi-version scheduler to abort and re-execute), and measures parallel-vs-sequential + * block execution time / TPS across worker counts. All numbers are written to {@code REPORT}. + */ +public class AereBlockStmBenchmarkTest extends AbstractParallelBlockProcessorIntegrationTest { + + private static final String BASE_GENESIS = + "/org/hyperledger/besu/ethereum/mainnet/parallelization/genesis-it.json"; + private static final Path REPORT = + Path.of(System.getProperty("java.io.tmpdir"), "blockstm_bench_results.txt"); + + // Compute-heavy runtime: PUSH1 0 CALLDATALOAD (iters); loop { KECCAK256 32 bytes of mem; MSTORE }; + // then SSTORE slot0=1; STOP. Iteration count is supplied per-tx via 32-byte calldata. + private static final String COMPUTE_CODE = + "0x6000355b60206000206000526001900380600357600160005500"; + + private static final int MAX_SENDERS = 1024; + private static final Wei BASE_FEE = Wei.of(5); + + private static GenesisConfig customGenesis; + private static KeyPair[] senders; + private static Address[] senderAddr; + private final List lines = new ArrayList<>(); + + private static Address recipient(final int i) { + return Address.fromHexString(String.format("0x%040x", 0x10000000L + i)); + } + + private static final Address SHARED_RECIPIENT = + Address.fromHexString(String.format("0x%040x", 0x0DEAD001L)); + + private static Address computeContract(final int i) { + return Address.fromHexString(String.format("0x%040x", 0x20000000L + i)); + } + + @BeforeAll + static void buildGenesis() throws Exception { + final String baseText; + try (InputStream is = AereBlockStmBenchmarkTest.class.getResourceAsStream(BASE_GENESIS)) { + baseText = new String(is.readAllBytes(), StandardCharsets.UTF_8); + } + final ObjectNode root = JsonUtil.objectNodeFromString(baseText); + final ObjectNode alloc = (ObjectNode) root.get("alloc"); + + senders = new KeyPair[MAX_SENDERS]; + senderAddr = new Address[MAX_SENDERS]; + final String richBalance = "0x" + new BigInteger("1000000000000000000000000").toString(16); + for (int i = 0; i < MAX_SENDERS; i++) { + final String pkHex = String.format("%064x", i + 1); + final KeyPair kp = ParallelBlockProcessorTestSupport.generateKeyPair(pkHex); + senders[i] = kp; + senderAddr[i] = Util.publicKeyToAddress(kp.getPublicKey()); + final ObjectNode acct = JsonUtil.createEmptyObjectNode(); + acct.put("balance", richBalance); + alloc.set(senderAddr[i].getBytes().toUnprefixedHexString(), acct); + + final ObjectNode contract = JsonUtil.createEmptyObjectNode(); + contract.put("balance", "0"); + contract.put("code", COMPUTE_CODE); + alloc.set(computeContract(i).getBytes().toUnprefixedHexString(), contract); + } + customGenesis = GenesisConfig.fromConfig(root); + Files.write( + REPORT, + ("AERE BLOCK-STM BENCHMARK / VERIFICATION REPORT\n" + + "generated on a scratch host (chain-independent block-processor grain)\n" + + "cpus(reported)=" + Runtime.getRuntime().availableProcessors() + + " loadAvg(start)=" + + String.format("%.2f", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()) + + "\n===============================================================\n") + .getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + + private void log(final String s) { + lines.add(s); + System.out.println("[BLOCKSTM-BENCH] " + s); + } + + private void flush() throws Exception { + Files.write( + REPORT, + (String.join("\n", lines) + "\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.APPEND); + lines.clear(); + } + + @Override + protected ExecutionContextTestFixture createFreshContext() { + return ExecutionContextTestFixture.builder(customGenesis) + .dataStorageFormat(DataStorageFormat.BONSAI) + .build(); + } + + @Override + protected String getVariantName() { + return "AERE Block-STM Benchmark"; + } + + @Override + protected ParallelTransactionPreprocessing createParallelPreprocessing( + final MainnetTransactionProcessor transactionProcessor) { + return new ParallelTransactionPreprocessing( + transactionProcessor, Runnable::run, getBalConfiguration()); + } + + // ---- workloads ---------------------------------------------------------- + + private Transaction[] independentTransfers(final int n) { + final Transaction[] txs = new Transaction[n]; + for (int i = 0; i < n; i++) { + txs[i] = + createTransferTransaction( + 0, 1L, 100_000L, 0L, 5L, recipient(i).toHexString(), senders[i]); + } + return txs; + } + + private Transaction[] lowConflictTransfers(final int n, final int pool) { + // Distinct senders credit recipients drawn from a small pool -> occasional write-conflict on a + // shared recipient balance (ERC20-style low contention). pool read/write conflict on that balance. + final Transaction[] txs = new Transaction[n]; + for (int i = 0; i < n; i++) { + txs[i] = + createTransferTransaction( + 0, 1L, 100_000L, 0L, 5L, SHARED_RECIPIENT.toHexString(), senders[i]); + } + return txs; + } + + private Transaction[] independentCompute(final int n, final long iters) { + final Bytes payload = UInt256.valueOf(iters).toBytes(); + final Transaction[] txs = new Transaction[n]; + for (int i = 0; i < n; i++) { + txs[i] = + Transaction.builder() + .type(TransactionType.EIP1559) + .nonce(0) + .maxPriorityFeePerGas(Wei.ZERO) + .maxFeePerGas(Wei.of(5)) + .gasLimit(30_000_000L) + .to(computeContract(i)) + .value(Wei.ZERO) + .payload(payload) + .chainId(BigInteger.valueOf(42)) + .signAndBuild(senders[i]); + } + return txs; + } + + // ---- block plumbing (configurable gas limit) ---------------------------- + + private Block buildBlock( + final BlockHeader parent, + final Hash stateRoot, + final long gasLimit, + final Transaction[] txs) { + final BlockHeader header = + new BlockHeaderTestFixture() + .number(parent.getNumber() + 1L) + .parentHash(parent.getHash()) + .coinbase(MINING_BENEFICIARY) + .stateRoot(stateRoot) + .gasLimit(gasLimit) + .baseFeePerGas(BASE_FEE) + .buildHeader(); + final BlockBody body = + new BlockBody(Arrays.asList(txs), Collections.emptyList(), Optional.empty()); + return new Block(header, body); + } + + private Hash discover(final long gasLimit, final Transaction[] txs) { + final ExecutionContextTestFixture ctx = createFreshContext(); + final BlockHeader parent = ctx.getBlockchain().getChainHeadHeader(); + final MutableWorldState ws = + ctx.getStateArchive() + .getWorldState(WorldStateQueryParams.withBlockHeaderAndUpdateNodeHead(parent)) + .orElseThrow(); + final Block block = buildBlock(parent, Hash.ZERO, gasLimit, txs); + final BlockProcessor seq = createSequentialProcessor(ctx); + final BlockProcessingResult r = + seq.processBlock(ctx.getProtocolContext(), ctx.getBlockchain(), ws, block); + if (r.isSuccessful()) { + return ws.rootHash(); + } + // Header stateRoot was ZERO, so processing "failed" on the root-mismatch check; recover the + // real (calculated) root from the error message: the first non-zero 32-byte hex it contains. + final String msg = r.errorMessage.orElseThrow(); + final java.util.regex.Matcher m = + java.util.regex.Pattern.compile("0x[0-9a-fA-F]{64}").matcher(msg); + while (m.find()) { + final Hash h = Hash.fromHexString(m.group()); + if (!h.equals(Hash.ZERO)) { + return h; + } + } + throw new AssertionError("could not recover state root from: " + msg); + } + + private BlockProcessor stockParallel(final ExecutionContextTestFixture ctx) { + final ProtocolSpec spec = + ctx.getProtocolSchedule() + .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()); + return new MainnetParallelBlockProcessor( + spec.getTransactionProcessor(), + spec.getTransactionReceiptFactory(), + Wei.ZERO, + BlockHeader::getCoinbase, + true, + ctx.getProtocolSchedule(), + getBalConfiguration(), + new NoOpMetricsSystem()); + } + + /** One timed sequential run. Returns [rootHex, nanos]. */ + private Object[] runSeq(final long gasLimit, final Hash stateRoot, final Transaction[] txs) { + final ExecutionContextTestFixture ctx = createFreshContext(); + final BlockHeader parent = ctx.getBlockchain().getChainHeadHeader(); + final MutableWorldState ws = + ctx.getStateArchive() + .getWorldState(WorldStateQueryParams.withBlockHeaderAndUpdateNodeHead(parent)) + .orElseThrow(); + final Block block = buildBlock(parent, stateRoot, gasLimit, txs); + final BlockProcessor seq = createSequentialProcessor(ctx); + final long t0 = System.nanoTime(); + final BlockProcessingResult r = + seq.processBlock(ctx.getProtocolContext(), ctx.getBlockchain(), ws, block); + final long dt = System.nanoTime() - t0; + assertTrue(r.isSuccessful(), "seq failed: " + r.errorMessage.orElse("?")); + return new Object[] {ws.rootHash(), dt, r}; + } + + /** One timed AERE Block-STM parallel run at {@code workers}. Returns [rootHex, nanos, nbParallel]. */ + private Object[] runPar( + final long gasLimit, final Hash stateRoot, final int workers, final Transaction[] txs) { + final ExecutionContextTestFixture ctx = createFreshContext(); + final BlockHeader parent = ctx.getBlockchain().getChainHeadHeader(); + final MutableWorldState ws = + ctx.getStateArchive() + .getWorldState(WorldStateQueryParams.withBlockHeaderAndUpdateNodeHead(parent)) + .orElseThrow(); + final Block block = buildBlock(parent, stateRoot, gasLimit, txs); + final ProtocolSpec spec = + ctx.getProtocolSchedule() + .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()); + final BlockProcessor par = stockParallel(ctx); + final AereBlockStmPreprocessing pf = + new AereBlockStmPreprocessing(spec.getTransactionProcessor(), workers); + final long t0 = System.nanoTime(); + final BlockProcessingResult r = + par.processBlock(ctx.getProtocolContext(), ctx.getBlockchain(), ws, block, pf); + final long dt = System.nanoTime() - t0; + assertTrue(r.isSuccessful(), "par failed: " + r.errorMessage.orElse("?")); + return new Object[] {ws.rootHash(), dt, r.getNbParallelizedTransactions().orElse(0)}; + } + + /** Drive the engine directly to read scheduler statistics (executions / aborts / verified). */ + private BlockStmStats engineStats(final long gasLimit, final int workers, final Transaction[] txs) { + final ExecutionContextTestFixture ctx = createFreshContext(); + final BlockHeader parent = ctx.getBlockchain().getChainHeadHeader(); + final Block block = buildBlock(parent, Hash.ZERO, gasLimit, txs); + final AereBlockStmEngine engine = + new AereBlockStmEngine( + ctx.getProtocolSchedule() + .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()) + .getTransactionProcessor(), + workers); + final BlockHashLookup zero = (frame, number) -> Hash.ZERO; + engine.runAsyncBlock( + ctx.getProtocolContext(), + block.getHeader(), + Arrays.asList(txs), + MINING_BENEFICIARY, + zero, + Wei.ZERO, + Runnable::run, + Optional.empty(), + Optional.of(parent)); + return engine.stats(); + } + + private static long median(final long[] a) { + final long[] c = a.clone(); + Arrays.sort(c); + return c[c.length / 2]; + } + + // ---- the suite ---------------------------------------------------------- + + @Test + void blockStmVerifyAndBenchmark() throws Exception { + final int cpus = Runtime.getRuntime().availableProcessors(); + final int[] workerCounts = {1, 2, 4, 8, 16}; + + // ===================== CORRECTNESS ===================== + log(""); + log("## PART 1 - CORRECTNESS (parallel root == sequential root)"); + + // 1a. Independent transfers: expect zero aborts, executions == n. + { + final int n = 300; + final Transaction[] txs = independentTransfers(n); + final Hash root = discover(30_000_000L, txs); + final Object[] seq = runSeq(30_000_000L, root, txs); + final Object[] par = runPar(30_000_000L, root, cpus, txs); + final Hash seqRoot = (Hash) seq[0]; + final Hash parRoot = (Hash) par[0]; + final int nbPar = (Integer) par[2]; + final BlockStmStats st = engineStats(30_000_000L, cpus, txs); + assertThat(parRoot).as("independent parallel root == sequential").isEqualTo(seqRoot); + log(String.format( + " [independent n=%d w=%d] parRoot==seqRoot: %s root=%s", n, cpus, + parRoot.equals(seqRoot), seqRoot.toHexString())); + log(String.format( + " nbParallelized=%d verified=%s execs=%d (n=%d) aborts=%d committedParallel=%d", + nbPar, st.verified(), st.totalExecutions(), st.numTxns(), st.totalAborts(), + st.committedParallel())); + assertThat(st.verified()).as("independent must verify").isTrue(); + assertThat(st.totalAborts()).as("independent must have zero aborts").isZero(); + assertThat(st.totalExecutions()).as("independent: one execution per tx").isEqualTo(n); + assertThat(nbPar).as("parallel path exercised").isGreaterThan(0); + } + + // 1b. Conflicting transfers (shared recipient): expect aborts>0, executions>n, root still equal. + { + final int n = 300; + final Transaction[] txs = conflictingTransfers(n); + final Hash root = discover(30_000_000L, txs); + final Object[] seq = runSeq(30_000_000L, root, txs); + final Object[] par = runPar(30_000_000L, root, cpus, txs); + final Hash seqRoot = (Hash) seq[0]; + final Hash parRoot = (Hash) par[0]; + final int nbPar = (Integer) par[2]; + final BlockStmStats st = engineStats(30_000_000L, cpus, txs); + assertThat(parRoot).as("CONFLICT parallel root == sequential").isEqualTo(seqRoot); + log(String.format( + " [conflict n=%d w=%d] parRoot==seqRoot: %s root=%s", n, cpus, + parRoot.equals(seqRoot), seqRoot.toHexString())); + log(String.format( + " nbParallelized=%d verified=%s execs=%d (n=%d, re-exec=%d) aborts=%d committedParallel=%d", + nbPar, st.verified(), st.totalExecutions(), st.numTxns(), + st.totalExecutions() - st.numTxns(), st.totalAborts(), st.committedParallel())); + assertThat(st.verified()).as("conflict must verify").isTrue(); + assertThat(st.totalAborts()).as("conflict must trigger re-execution (aborts>0)").isGreaterThan(0); + assertThat(st.totalExecutions()).as("conflict re-executes (execs>n)").isGreaterThan(n); + } + + // 1c. Independent compute contract calls: root equal, zero aborts. + { + final int n = 128; + final long iters = 400; + final Transaction[] txs = independentCompute(n, iters); + final Hash root = discover(3_000_000_000L, txs); + final Object[] seq = runSeq(3_000_000_000L, root, txs); + final Object[] par = runPar(3_000_000_000L, root, cpus, txs); + final Hash seqRoot = (Hash) seq[0]; + final Hash parRoot = (Hash) par[0]; + final BlockStmStats st = engineStats(3_000_000_000L, cpus, txs); + assertThat(parRoot).as("compute parallel root == sequential").isEqualTo(seqRoot); + log(String.format( + " [compute n=%d w=%d iters=%d] parRoot==seqRoot: %s root=%s", n, cpus, iters, + parRoot.equals(seqRoot), seqRoot.toHexString())); + log(String.format( + " verified=%s execs=%d (n=%d) aborts=%d committedParallel=%d", + st.verified(), st.totalExecutions(), st.numTxns(), st.totalAborts(), + st.committedParallel())); + assertThat(st.verified()).isTrue(); + assertThat(st.totalAborts()).isZero(); + } + + // 1d. Low-conflict transfers (recipient pool): root equal, verified (aborts allowed but rare). + { + final int n = 300; + final Transaction[] txs = lowConflictTransfers(n, 270); + final Hash root = discover(30_000_000L, txs); + final Object[] seq = runSeq(30_000_000L, root, txs); + final Object[] par = runPar(30_000_000L, root, cpus, txs); + final Hash seqRoot = (Hash) seq[0]; + final Hash parRoot = (Hash) par[0]; + final int nbPar = (Integer) par[2]; + final BlockStmStats st = engineStats(30_000_000L, cpus, txs); + assertThat(parRoot).as("low-conflict parallel root == sequential").isEqualTo(seqRoot); + log(String.format( + " [low-conflict n=%d w=%d pool=270] parRoot==seqRoot: %s root=%s", n, cpus, + parRoot.equals(seqRoot), seqRoot.toHexString())); + log(String.format( + " nbParallelized=%d verified=%s execs=%d (n=%d, re-exec=%d) aborts=%d committedParallel=%d", + nbPar, st.verified(), st.totalExecutions(), st.numTxns(), + st.totalExecutions() - st.numTxns(), st.totalAborts(), st.committedParallel())); + assertThat(st.verified()).as("low-conflict must verify").isTrue(); + } + flush(); + + // ===================== THROUGHPUT ===================== + final int warmup = 3; + final int measure = 5; + + // Workload A: independent transfers. + runThroughput( + "PART 2A - THROUGHPUT: independent transfers", 1000, 30_000_000L, + independentTransfers(1000), workerCounts, warmup, measure); + + // Workload A': independent compute-heavy calls (execution-bound; best case for parallelism). + runThroughput( + "PART 2B - THROUGHPUT: independent compute calls (iters=800)", 256, 3_000_000_000L, + independentCompute(256, 800), workerCounts, warmup, measure); + + // Workload C: high-conflict transfers (all to one recipient) - expect no speedup. + runThroughput( + "PART 2C - THROUGHPUT: high-conflict transfers (shared recipient)", 500, 30_000_000L, + conflictingTransfers(500), workerCounts, warmup, measure); + + // Workload D: low-conflict transfers (recipient pool=900 of 1000) - ERC20-style ~10% contention. + runThroughput( + "PART 2D - THROUGHPUT: low-conflict transfers (recipient pool=900)", 1000, 30_000_000L, + lowConflictTransfers(1000, 900), workerCounts, warmup, measure); + + // ===================== ISOLATED EXECUTION PHASE ===================== + // The real Block-STM test: time ONLY the parallel execution phase (engine.runAsyncBlock, i.e. + // the flat-snapshot base reads + concurrent EVM execution + MV bookkeeping), measured INSIDE the + // engine (execPhaseNanos), with the serial Bonsai trie commit excluded entirely. Execution-bound + // independent compute calls so there is real work to parallelize; scaling vs w=1 is the proof. + runExecPhase( + "PART 3A - EXECUTION PHASE (isolated): independent compute calls (iters=800)", + 256, 3_000_000_000L, independentCompute(256, 800), workerCounts, warmup, measure); + runExecPhase( + "PART 3B - EXECUTION PHASE (isolated): independent compute calls (iters=3000, heavy)", + 256, 3_000_000_000L, independentCompute(256, 3000), workerCounts, warmup, measure); + + log(""); + log("loadAvg(end)=" + + String.format("%.2f", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage())); + log("=============================== END ==============================="); + flush(); + } + + private void runThroughput( + final String title, + final int n, + final long gasLimit, + final Transaction[] txs, + final int[] workerCounts, + final int warmup, + final int measure) + throws Exception { + log(""); + log("## " + title + " (n=" + n + " txs)"); + final Hash root = discover(gasLimit, txs); + + // Sequential baseline. + for (int i = 0; i < warmup; i++) { + runSeq(gasLimit, root, txs); + } + final long[] seqT = new long[measure]; + for (int i = 0; i < measure; i++) { + seqT[i] = (Long) runSeq(gasLimit, root, txs)[1]; + } + final long seqMed = median(seqT); + final double seqMs = seqMed / 1e6; + final double seqTps = n / (seqMed / 1e9); + log(String.format( + " SEQUENTIAL (MainnetBlockProcessor): %8.3f ms %10.0f tx/s", seqMs, seqTps)); + log(" worker | median ms | tx/s | speedup vs seq | scaling vs w=1"); + log(" -------+------------+----------+----------------+---------------"); + + double par1Ms = Double.NaN; + for (final int w : workerCounts) { + for (int i = 0; i < warmup; i++) { + runPar(gasLimit, root, w, txs); + } + final long[] pt = new long[measure]; + int nbPar = 0; + for (int i = 0; i < measure; i++) { + final Object[] r = runPar(gasLimit, root, w, txs); + pt[i] = (Long) r[1]; + nbPar = (Integer) r[2]; + } + final long med = median(pt); + final double ms = med / 1e6; + final double tps = n / (med / 1e9); + if (w == 1) { + par1Ms = ms; + } + log(String.format( + " %6d | %10.3f | %8.0f | %13.2fx | %12.2fx (nbParallel=%d)", + w, ms, tps, seqMs / ms, par1Ms / ms, nbPar)); + } + flush(); + } + + /** + * Measures the ISOLATED parallel execution phase across worker counts. Drives the engine directly + * (runAsyncBlock) and reads {@code execPhaseNanos} recorded INSIDE the engine (worker pool start -> + * termination), so the flat-snapshot mint and the downstream serial Bonsai trie commit are both + * excluded. Scaling vs w=1 of this number is the direct test of whether Block-STM execution + * parallelism works once the per-worker snapshot overhead is removed. + */ + private void runExecPhase( + final String title, + final int n, + final long gasLimit, + final Transaction[] txs, + final int[] workerCounts, + final int warmup, + final int measure) + throws Exception { + log(""); + log("## " + title + " (n=" + n + " txs)"); + log(" worker | exec-phase ms | snapshot ms | exec tx/s | scaling vs w=1 | verified"); + log(" -------+----------------+-------------+--------------+----------------+---------"); + + double w1Ms = Double.NaN; + for (final int w : workerCounts) { + for (int i = 0; i < warmup; i++) { + engineStats(gasLimit, w, txs); + } + final long[] execT = new long[measure]; + final long[] snapT = new long[measure]; + boolean verified = true; + for (int i = 0; i < measure; i++) { + final BlockStmStats st = engineStats(gasLimit, w, txs); + execT[i] = st.execPhaseNanos(); + snapT[i] = st.snapshotNanos(); + verified = verified && st.verified(); + } + final long execMed = median(execT); + final long snapMed = median(snapT); + final double execMs = execMed / 1e6; + final double snapMs = snapMed / 1e6; + final double tps = n / (execMed / 1e9); + if (w == 1) { + w1Ms = execMs; + } + assertThat(verified).as("exec-phase run must verify at w=" + w).isTrue(); + log(String.format( + " %6d | %14.3f | %11.3f | %12.0f | %13.2fx | %s", + w, execMs, snapMs, tps, w1Ms / execMs, verified)); + } + flush(); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmSmokeTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmSmokeTest.java new file mode 100644 index 0000000000..a8e3db99de --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/AereBlockStmSmokeTest.java @@ -0,0 +1,130 @@ +/* + * Copyright contributors to 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 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_2; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_3; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_4; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_1_KEYPAIR; +import static org.hyperledger.besu.ethereum.mainnet.parallelization.ParallelBlockProcessorTestSupport.ACCOUNT_GENESIS_2_KEYPAIR; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.BlockProcessingResult; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.PreprocessingFunction; +import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.blockstm.AereBlockStmPreprocessing; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * Smoke test proving AERE Block-STM parallel execution is serial-equivalent: a block processed + * through the AERE Block-STM engine (a stock {@link MainnetParallelBlockProcessor} driven by {@link + * AereBlockStmPreprocessing}) yields a state root bit-identical to sequential {@code + * MainnetBlockProcessor} execution of the same block, and the parallel path is actually exercised. + */ +public class AereBlockStmSmokeTest extends AbstractParallelBlockProcessorIntegrationTest { + + @Override + protected String getVariantName() { + return "AERE Block-STM"; + } + + @Override + protected ParallelTransactionPreprocessing createParallelPreprocessing( + final MainnetTransactionProcessor transactionProcessor) { + // Not used by this test; provided to satisfy the abstract base. + return new ParallelTransactionPreprocessing( + transactionProcessor, Runnable::run, getBalConfiguration()); + } + + private BlockProcessor stockParallelProcessor(final ExecutionContextTestFixture ctx) { + final ProtocolSpec spec = + ctx.getProtocolSchedule() + .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()); + return new MainnetParallelBlockProcessor( + spec.getTransactionProcessor(), + spec.getTransactionReceiptFactory(), + Wei.ZERO, + BlockHeader::getCoinbase, + true, + ctx.getProtocolSchedule(), + getBalConfiguration(), + new NoOpMetricsSystem()); + } + + @Test + @DisplayName("AERE Block-STM parallel root equals sequential root for independent transfers") + void blockStmRootMatchesSequential() { + final Transaction tx1 = + createTransferTransaction( + 0, 1_000_000_000_000_000_000L, 300_000L, 0L, 5L, ACCOUNT_2, ACCOUNT_GENESIS_1_KEYPAIR); + final Transaction tx2 = + createTransferTransaction( + 0, 2_000_000_000_000_000_000L, 300_000L, 0L, 5L, ACCOUNT_3, ACCOUNT_GENESIS_2_KEYPAIR); + final Transaction tx3 = + createTransferTransaction( + 1, 3_000_000_000_000_000_000L, 300_000L, 0L, 5L, ACCOUNT_4, ACCOUNT_GENESIS_1_KEYPAIR); + + final Wei baseFee = Wei.of(5); + final Transaction[] txs = {tx1, tx2, tx3}; + final Hash stateRoot = discoverStateRoot(baseFee, txs); + + // Sequential + final ExecutionContextTestFixture seqCtx = createFreshContext(); + final MutableWorldState seqWs = seqCtx.getStateArchive().getWorldState(); + final Block seqBlock = createBlock(seqCtx, stateRoot, baseFee, txs); + final BlockProcessor seqProcessor = createSequentialProcessor(seqCtx); + final BlockProcessingResult seqResult = + seqProcessor.processBlock( + seqCtx.getProtocolContext(), seqCtx.getBlockchain(), seqWs, seqBlock); + assertTrue(seqResult.isSuccessful(), "sequential processing failed"); + + // Parallel via AERE Block-STM + final ExecutionContextTestFixture parCtx = createFreshContext(); + final MutableWorldState parWs = parCtx.getStateArchive().getWorldState(); + final Block parBlock = createBlock(parCtx, stateRoot, baseFee, txs); + final ProtocolSpec spec = + parCtx + .getProtocolSchedule() + .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()); + final BlockProcessor parProcessor = stockParallelProcessor(parCtx); + final PreprocessingFunction blockStm = + new AereBlockStmPreprocessing( + spec.getTransactionProcessor(), Runtime.getRuntime().availableProcessors()); + final BlockProcessingResult parResult = + parProcessor.processBlock( + parCtx.getProtocolContext(), parCtx.getBlockchain(), parWs, parBlock, blockStm); + assertTrue( + parResult.isSuccessful(), + "AERE Block-STM processing failed: " + parResult.errorMessage.orElse("(no message)")); + + // Serial-equivalence: bit-identical 32-byte state root. + assertThat(parWs.rootHash()) + .as("AERE Block-STM parallel state root must equal sequential") + .isEqualTo(seqWs.rootHash()); + + // The parallel path was actually exercised (outcomes consumed, not per-tx sequential fallback). + assertParallelizationRecordedInResults(seqResult, parResult, txs.length); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java index ee4e941111..92ec415271 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-cap-prestatus-rlpx-connections.patch. */ package org.hyperledger.besu.ethereum.eth.manager; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.SnapProtocol; import org.hyperledger.besu.ethereum.eth.manager.EthPeer.DisconnectCallback; import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerSelector; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.ChainHeadTracker; import org.hyperledger.besu.ethereum.eth.sync.SnapServerChecker; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.TrailingPeerRequirements; import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.p2p.peers.Peer; import org.hyperledger.besu.ethereum.p2p.peers.PeerId; import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; @@ -74,46 +80,55 @@ public class EthPeers implements PeerSelector { Comparator.comparing((final EthPeerImmutableAttributes p) -> p.estimatedTotalDifficulty()); public static final Comparator CHAIN_HEIGHT = Comparator.comparing((final EthPeerImmutableAttributes p) -> p.estimatedChainHeight()); public static final Comparator MOST_USEFUL_PEER = Comparator.comparing((final EthPeerImmutableAttributes p) -> p.reputationScore()) .thenComparing(CHAIN_HEIGHT); public static final Comparator TOTAL_DIFFICULTY_THEN_HEIGHT = TOTAL_DIFFICULTY.thenComparing(CHAIN_HEIGHT); public static final Comparator LEAST_TO_MOST_BUSY = Comparator.comparing(EthPeerImmutableAttributes::outstandingRequests) .thenComparing(EthPeerImmutableAttributes::lastRequestTimestamp); public static final int NODE_ID_LENGTH = 64; public static final int USEFULL_PEER_SCORE_THRESHOLD = 102; private final Map activeConnections = new ConcurrentHashMap<>(); - private final Cache incompleteConnections = - CacheBuilder.newBuilder() - .expireAfterWrite(Duration.ofSeconds(20L)) - .concurrencyLevel(1) - .removalListener(this::onCacheRemoval) - .build(); + /** + * Lower bound for the pre-STATUS (incomplete) connection cap, so that even very small {@code + * --max-peers} values still tolerate a reasonable number of concurrent inbound handshakes. AERE + * D-434: ported from upstream hyperledger/besu e540ae2394 (GHSA-6r9q-wjp4-34gh). + */ + private static final int INCOMPLETE_CONNECTIONS_CAP_FLOOR = 10; + + /** + * Maximum number of connections that have completed the devp2p HELLO but not yet the eth STATUS + * handshake that we retain. Bounds file-descriptor and heap growth from peers that connect and + * never send STATUS, which are otherwise invisible to {@code --max-peers} accounting. + */ + private final int maxIncompleteConnections; + + private final Cache incompleteConnections; private final Clock clock; private final List permissioningProviders; private final int maxMessageSize; private final Subscribers connectCallbacks = Subscribers.create(); private final Subscribers disconnectCallbacks = Subscribers.create(); private final Collection pendingRequests = new CopyOnWriteArrayList<>(); private final int peerUpperBound; private final int maxRemotelyInitiatedConnections; private final Boolean randomPeerPriority; private final Bytes nodeIdMask = Bytes.random(NODE_ID_LENGTH); private final Supplier currentProtocolSpecSupplier; private final SyncMode syncMode; private final ForkIdManager forkIdManager; private final int snapServerTargetNumber; private final boolean shouldLimitRemoteConnections; private Comparator bestPeerComparator; private final Bytes localNodeId; private RlpxAgent rlpxAgent; @@ -134,62 +149,75 @@ public class EthPeers implements PeerSelector { final int peerUpperBound, final int maxRemotelyInitiatedConnections, final Boolean randomPeerPriority, final SyncMode syncMode, final ForkIdManager forkIdManager) { this.currentProtocolSpecSupplier = currentProtocolSpecSupplier; this.clock = clock; this.permissioningProviders = permissioningProviders; this.maxMessageSize = maxMessageSize; this.bestPeerComparator = TOTAL_DIFFICULTY_THEN_HEIGHT; this.localNodeId = localNodeId; this.peerUpperBound = peerUpperBound; this.maxRemotelyInitiatedConnections = maxRemotelyInitiatedConnections; this.randomPeerPriority = randomPeerPriority; LOG.trace("MaxPeers: {}, Max Remote: {}", peerUpperBound, maxRemotelyInitiatedConnections); this.syncMode = syncMode; this.forkIdManager = forkIdManager; this.snapServerTargetNumber = peerUpperBound / 2; // 50% of peers should be snap servers while snap syncing this.shouldLimitRemoteConnections = maxRemotelyInitiatedConnections < peerUpperBound; + this.maxIncompleteConnections = Math.max(peerUpperBound * 2, INCOMPLETE_CONNECTIONS_CAP_FLOOR); + this.incompleteConnections = + CacheBuilder.newBuilder() + .maximumSize(maxIncompleteConnections) + .expireAfterWrite(Duration.ofSeconds(20L)) + .concurrencyLevel(1) + .removalListener(this::onCacheRemoval) + .build(); metricsSystem.createIntegerGauge( BesuMetricCategory.ETHEREUM, "peer_count", "The current number of peers connected", activeConnections::size); metricsSystem.createIntegerGauge( BesuMetricCategory.ETHEREUM, "peer_count_snap_server", "The current number of peers connected that serve snap data", () -> (int) streamAvailablePeers().filter(EthPeerImmutableAttributes::isServingSnap).count()); metricsSystem.createIntegerGauge( BesuMetricCategory.PEERS, "pending_peer_requests_current", "Number of peer requests currently pending because peers are busy", pendingRequests::size); metricsSystem.createIntegerGauge( BesuMetricCategory.ETHEREUM, "peer_limit", "The maximum number of peers this node allows to connect", () -> peerUpperBound); + metricsSystem.createIntegerGauge( + BesuMetricCategory.ETHEREUM, + "peer_count_incomplete", + "The current number of connections that have not yet completed the eth STATUS handshake", + () -> (int) incompleteConnections.size()); connectedPeersCounter = metricsSystem.createCounter( BesuMetricCategory.PEERS, "connected_total", "Total number of peers connected"); final LabelledSuppliedMetric peerClientLabelledGauge = metricsSystem.createLabelledSuppliedGauge( BesuMetricCategory.PEERS, "peer_count_by_client", "The number of clients connected by client", "client"); for (final var clientName : PeerClientName.values()) { peerClientLabelledGauge.labels( () -> countConnectedPeersByClientName(clientName), clientName.getDisplayName()); } } private double countConnectedPeersByClientName(final PeerClientName clientName) { return streamAllActiveConnections() @@ -737,56 +765,86 @@ public class EthPeers implements PeerSelector { conn, peerUpperBound); conn.disconnect(DisconnectMessage.DisconnectReason.TOO_MANY_PEERS); }); } private boolean inboundInitiatedConnectionLimitExceeded() { return shouldLimitRemoteConnections && countUntrustedRemotelyInitiatedConnections() > maxRemotelyInitiatedConnections; } private long countUntrustedRemotelyInitiatedConnections() { return activeConnections.values().stream() .map(EthPeer::getConnection) .filter(PeerConnection::inboundInitiated) .filter(c -> !c.isDisconnected()) .filter(conn -> !canExceedPeerLimits(conn.getPeer().getId())) .count(); } - private void onCacheRemoval( - final RemovalNotification removalNotification) { - if (removalNotification.wasEvicted()) { - final PeerConnection peerConnectionRemoved = removalNotification.getKey(); - final EthPeer peer = removalNotification.getValue(); - if (peer == null) { - return; - } - final PeerConnection peerConnectionOfEthPeer = peer.getConnection(); - if (peerConnectionRemoved != null) { - if (!peerConnectionRemoved.equals(peerConnectionOfEthPeer)) { - // If this connection is not the connection of the EthPeer by now we can disconnect - peerConnectionRemoved.disconnect(DisconnectMessage.DisconnectReason.ALREADY_CONNECTED); - } - } + // AERE D-434 (e540ae2394, GHSA-6r9q-wjp4-34gh): close pre-STATUS connections evicted from the + // bounded incomplete-connection cache so they cannot leak file descriptors or heap. + @VisibleForTesting + void onCacheRemoval(final RemovalNotification removalNotification) { + // Only react to evictions (size cap or expiry). Explicit invalidations (e.g. on a normal + // disconnect) already close the connection through their own path. + if (!removalNotification.wasEvicted()) { + return; + } + final PeerConnection evictedConnection = removalNotification.getKey(); + final EthPeer peer = removalNotification.getValue(); + if (evictedConnection == null || evictedConnection.isDisconnected()) { + return; } + + final boolean isCurrentConnectionOfPeer = + peer != null && evictedConnection.equals(peer.getConnection()); + if (isCurrentConnectionOfPeer && peer.statusHasBeenReceived()) { + // The peer completed (or is completing) the eth STATUS handshake and is being promoted to an + // active connection; its incomplete-cache entry is expiring naturally. Leave the live + // connection alone - it is (or will be) tracked in activeConnections. + return; + } + + // Either a superseded connection (the peer reconnected with a different connection), or a + // connection that completed the devp2p HELLO but never sent eth STATUS and has now been evicted + // (20s expiry, or pushed out of the bounded cache). Close the socket so evicted pre-STATUS + // connections cannot leak file descriptors or heap while remaining invisible to --max-peers. + DisconnectReason reason = + isCurrentConnectionOfPeer + ? DisconnectMessage.DisconnectReason.TIMEOUT + : DisconnectMessage.DisconnectReason.ALREADY_CONNECTED; + + LOG.atTrace() + .setMessage( + "Closing pre-STATUS connection {} evicted from incomplete-connection cache, reason {}") + .addArgument(evictedConnection::getPeerInfo) + .addArgument(reason) + .log(); + + evictedConnection.disconnect(reason); + } + + @VisibleForTesting + int incompleteConnectionCount() { + return (int) incompleteConnections.size(); } boolean addPeerToEthPeers(final EthPeer peer) { // We have a connection to a peer that is on the right chain and is willing to connect to us. // Figure out whether we want to add it to the active connections. final PeerConnection connection = peer.getConnection(); if (activeConnections.containsValue(peer)) { return false; } final Bytes id = peer.getId(); if (!randomPeerPriority) { if (peerCount() >= peerUpperBound) { final long numSnapServers = numberOfSnapServers(); final boolean inboundLimitExceeded = inboundInitiatedConnectionLimitExceeded(); // three reasons why we would disconnect an existing peer to accommodate the new peer if (canExceedPeerLimits(id) || (snapServerPeersNeeded && numSnapServers < snapServerTargetNumber diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManager.java index 7fde3a17c7..dbcc8fbacc 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManager.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/0008-aere-pq-aip22.patch. */ package org.hyperledger.besu.ethereum.eth.sync; import static org.hyperledger.besu.util.FutureUtils.exceptionallyCompose; import org.hyperledger.besu.consensus.merge.ForkchoiceEvent; import org.hyperledger.besu.consensus.merge.UnverifiedForkchoiceListener; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.BadBlockCause; import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthMessage; @@ -704,40 +710,53 @@ public class BlockPropagationManager implements UnverifiedForkchoiceListener { if (!protocolContext .getBlockchain() .contains(lowestPendingBlock.getHeader().getParentHash())) { requestParentBlock(lowestPendingBlock); } else { LOG.trace("Parent block is already in the chain"); // if the parent is already imported, process its children maybeProcessPendingChildrenBlocks(lowestPendingBlock.getHeader()); } } } private CompletableFuture validateAndProcessPendingBlock( final BlockHeaderValidator blockHeaderValidator, final Block block, final BlockHeader parent, final BadBlockManager badBlockManager) { final HeaderValidationMode validationMode = HeaderValidationMode.FULL; if (blockHeaderValidator.validateHeader( block.getHeader(), parent, protocolContext, validationMode)) { + // AERE D-412 (2026-09-26), Rule C: a valid header is not, from AIP-22 stage 5, a proof that a + // quorum agreed the block (it carries only the proposer's single Falcon seal). A validator does + // not ADOPT a propagated block at the tip of its own consensus height without a post-quantum + // commit quorum for it; the gate is inert below H_hdr and for non-validators, and the sync + // downloader (a different path) is untouched, so this is fork choice, not validation. + if (!PropagatedBlockAdoptionGate.mayAdopt(block.getHeader(), parent)) { + processingBlocksManager.registerBlockImportDone(block.getHash()); + LOG.info( + "AERE D-412: not adopting propagated block {} at the tip without a post-quantum commit " + + "quorum; this node's own consensus decides this height", + block.toLogString()); + return CompletableFuture.completedFuture(block); + } ethContext.getScheduler().scheduleSyncWorkerTask(() -> broadcastBlock(block, parent)); return runImportTask(block); } else { processingBlocksManager.registerBlockImportDone(block.getHash()); final String description = String.format("Failed header validation (%s)", validationMode); badBlockManager.addBadBlock(block, BadBlockCause.fromValidationFailure(description)); LOG.warn( "Added to bad block manager for invalid header, failed to import announced block {}", block.toLogString()); return CompletableFuture.completedFuture(block); } } private CompletableFuture runImportTask(final Block block) { final PersistBlockTask importTask = PersistBlockTask.create( protocolSchedule, protocolContext, ethContext, block, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/PropagatedBlockAdoptionGate.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/PropagatedBlockAdoptionGate.java new file mode 100644 index 0000000000..7e11ed3686 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/PropagatedBlockAdoptionGate.java @@ -0,0 +1,103 @@ +/* + * 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.ethereum.eth.sync; + +import org.hyperledger.besu.ethereum.core.BlockHeader; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AERE D-412 (2026-09-26), Rule C. A process-wide gate the block PROPAGATION path asks before it adopts + * a block arriving from the network as the new canonical head. + * + *

    WHY IT EXISTS. From AIP-22 stage 5 (H_hdr) a header carries only the proposer's single Falcon-512 + * seal, so a valid header no longer proves a quorum agreed the block: an honest committed block and a + * block a single key forged are indistinguishable to header validation. The quorum agreement lives OFF + * the header, in the commit seals a validator heard (AIP-21, {@code PqSealCache}). Measured on rep28098, + * a single validator forged its own between-anchor block, gossiped it, and honest validators imported it + * as head and built on top. This gate lets a validator refuse to ADOPT such a propagated block at the tip + * of its own consensus height unless it holds a post-quantum commit quorum for it. Block VALIDITY is + * unchanged (this is fork choice, not validation), so the sync downloader - a different code path that + * does not consult this gate - still imports the same chain and catch-up is unaffected. + * + *

    WHY A STATIC REGISTRY. This module ({@code ethereum/eth}) sits below the consensus modules and + * cannot depend on them, so the smart part of the decision (the seal cache, the registry, the validator + * set) lives in a consensus-aware implementation that registers itself here at startup, exactly as + * {@code PqSealCache.instance()} and the other AERE consensus singletons are process-wide. The interface + * takes only {@code ethereum/core} types, so there is no backward dependency. Unset, the gate is + * {@link #ALLOW_ALL} and behaviour is byte-for-byte as before. + * + *

    FAIL OPEN. {@link #mayAdopt} never throws: a bug in the installed gate reverts to the current + * behaviour (adopt), never to refusing every block, because a repair that could stop the chain is worse + * than the exposure it closes. + */ +public final class PropagatedBlockAdoptionGate { + + private static final Logger LOG = LoggerFactory.getLogger(PropagatedBlockAdoptionGate.class); + + /** The decision a consensus-aware implementation supplies. */ + @FunctionalInterface + public interface Gate { + /** + * Whether this node may adopt a block that arrived by propagation as the new canonical head. + * + * @param header the propagated block's header, already header-validated + * @param parent the parent header, already on this node's chain + * @return true to adopt (import as head), false to leave this height to this node's own consensus + */ + boolean mayAdopt(BlockHeader header, BlockHeader parent); + } + + /** The default: adopt everything, i.e. exactly the behaviour before this gate existed. */ + public static final Gate ALLOW_ALL = (header, parent) -> true; + + private static volatile Gate gate = ALLOW_ALL; + + private PropagatedBlockAdoptionGate() {} + + /** + * Install the gate for this process. A consensus builder calls this at startup. + * + * @param g the gate, or null to reset to {@link #ALLOW_ALL} + */ + public static void install(final Gate g) { + gate = (g == null) ? ALLOW_ALL : g; + } + + /** Reset to {@link #ALLOW_ALL}. For tests and a deliberate operator reset. */ + public static void reset() { + gate = ALLOW_ALL; + } + + /** + * Whether the installed gate allows adopting this propagated block. Never throws: on any error it + * allows (fail open), so a gate bug can never stop the chain. + * + * @param header the propagated block's header + * @param parent the parent header + * @return true to adopt + */ + public static boolean mayAdopt(final BlockHeader header, final BlockHeader parent) { + try { + return gate.mayAdopt(header, parent); + } catch (final RuntimeException e) { + LOG.warn( + "AERE D-412: propagation adoption gate threw ({}); allowing the block, as before the gate.", + e.toString()); + return true; + } + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncState.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncState.java old mode 100644 new mode 100755 index ab09fafaa0..9d5cdadf39 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncState.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncState.java @@ -1,65 +1,77 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-syncstate-in-sync-lock.patch. */ package org.hyperledger.besu.ethereum.eth.sync.state; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.ChainHead; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.DefaultSyncStatus; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer.InSyncListener; import org.hyperledger.besu.ethereum.eth.manager.ChainHeadEstimate; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.sync.common.checkpoint.Checkpoint; import org.hyperledger.besu.ethereum.eth.sync.worldstate.WorldStateDownloadStatus; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.plugin.data.SyncStatus; import org.hyperledger.besu.plugin.services.BesuEvents.InitialSyncCompletionListener; import org.hyperledger.besu.plugin.services.BesuEvents.SyncStatusListener; import org.hyperledger.besu.plugin.services.BesuEvents.TTDReachedListener; import org.hyperledger.besu.util.Subscribers; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; public class SyncState { private final Blockchain blockchain; private final EthPeers ethPeers; + // Ensures checkInSync() re-evaluation gives a consistent view of sync status. A + // standalone lock is used instead of the object monitor to prevent checkInSync() + // causing a deadlock while synchronized on the object monitor. + // (AERE D-426: upstream hyperledger/besu#11041, ported) + private final Object inSyncLock = new Object(); + private final AtomicLong inSyncSubscriberId = new AtomicLong(); private final Map inSyncTrackers = new ConcurrentHashMap<>(); private final Subscribers syncStatusListeners = Subscribers.create(); private final Subscribers ttdReachedListeners = Subscribers.create(); private final Subscribers completionListenerSubscribers = Subscribers.create(); private volatile long chainHeightListenerId; private volatile Optional syncTarget = Optional.empty(); private Optional worldStateDownloadStatus = Optional.empty(); private Optional newPeerListenerId; private Optional reachedTerminalDifficulty = Optional.empty(); private final Optional checkpoint; private volatile boolean isInitialSyncPhaseDone; private volatile boolean isResyncNeeded; private Optional

    maybeAccountToRepair = Optional.empty(); @@ -285,56 +297,59 @@ public class SyncState { } public long getLocalChainHeight() { return blockchain.getChainHeadBlockNumber(); } public long bestChainHeight() { final long localChainHeight = blockchain.getChainHeadBlockNumber(); return bestChainHeight(localChainHeight); } public long bestChainHeight(final long localChainHeight) { return Math.max( localChainHeight, ethPeers .bestPeerWithHeightEstimate() .map(p -> p.chainState().getEstimatedHeight()) .orElse(localChainHeight)); } - private synchronized void checkInSync() { - final ChainHead localChain = getLocalChainHead(); - final Optional syncTargetChain = getSyncTargetChainHead(); - final Optional bestPeerChain = getBestPeerChainHead(); - - // Remove listener when we've found a peer. - newPeerListenerId.ifPresent( - listenerId -> { - ethPeers.unsubscribeConnect(listenerId); - newPeerListenerId = Optional.empty(); - }); - - inSyncTrackers - .values() - .forEach( - (syncTracker) -> syncTracker.checkState(localChain, syncTargetChain, bestPeerChain)); + /** Evaluates whether this node is in sync and notifies any tracker whose verdict changed. */ + private void checkInSync() { + synchronized (inSyncLock) { + final ChainHead localChain = getLocalChainHead(); + final Optional syncTargetChain = getSyncTargetChainHead(); + final Optional bestPeerChain = getBestPeerChainHead(); + + // Remove listener when we've found a peer. + newPeerListenerId.ifPresent( + listenerId -> { + ethPeers.unsubscribeConnect(listenerId); + newPeerListenerId = Optional.empty(); + }); + + inSyncTrackers + .values() + .forEach( + (syncTracker) -> syncTracker.checkState(localChain, syncTargetChain, bestPeerChain)); + } } public Optional getCheckpoint() { return checkpoint; } public boolean isInitialSyncPhaseDone() { return isInitialSyncPhaseDone; } public void markInitialSyncPhaseAsDone() { isInitialSyncPhaseDone = true; isResyncNeeded = false; completionListenerSubscribers.forEach(InitialSyncCompletionListener::onInitialSyncCompleted); } public boolean isResyncNeeded() { return isResyncNeeded; } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java index ba6fd7b3dd..5b58648df1 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-syncstate-in-sync-lock.patch. */ package org.hyperledger.besu.ethereum.eth.sync.state; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; @@ -39,42 +45,47 @@ import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Synchronizer.InSyncListener; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.ChainHeadEstimate; import org.hyperledger.besu.ethereum.eth.manager.ChainState; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestBuilder; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.sync.common.checkpoint.Checkpoint; import org.hyperledger.besu.ethereum.eth.sync.common.checkpoint.ImmutableCheckpoint; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.plugin.data.SyncStatus; import org.hyperledger.besu.plugin.services.BesuEvents.InitialSyncCompletionListener; import org.hyperledger.besu.plugin.services.BesuEvents.SyncStatusListener; import org.hyperledger.besu.plugin.services.BesuEvents.TTDReachedListener; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class SyncStateTest { private static final Difficulty standardDifficultyPerBlock = Difficulty.ONE; private static final long OUR_CHAIN_HEAD_NUMBER = 3; private static final Difficulty OUR_CHAIN_DIFFICULTY = standardDifficultyPerBlock.multiply(OUR_CHAIN_HEAD_NUMBER); private static final long TARGET_CHAIN_DELTA = 20; private static final long TARGET_CHAIN_HEIGHT = OUR_CHAIN_HEAD_NUMBER + TARGET_CHAIN_DELTA; private static final Difficulty TARGET_DIFFICULTY = standardDifficultyPerBlock.multiply(TARGET_CHAIN_HEIGHT); @@ -527,40 +538,117 @@ public class SyncStateTest { } private EthPeer mockChainIsBetterThan(final EthPeer peer, final boolean isBetter) { final ChainState chainState = spy(peer.chainState()); final ChainHeadEstimate chainStateSnapshot = spy(peer.chainStateSnapshot()); lenient().doReturn(isBetter).when(chainState).chainIsBetterThan(any()); lenient().doReturn(isBetter).when(chainStateSnapshot).chainIsBetterThan(any()); final EthPeer mockedPeer = spy(peer); lenient().doReturn(chainStateSnapshot).when(chainState).getSnapshot(); lenient().doReturn(chainStateSnapshot).when(mockedPeer).chainStateSnapshot(); lenient().doReturn(chainState).when(mockedPeer).chainState(); return mockedPeer; } private void setupOutOfSyncState() { syncState.setSyncTarget(syncTargetPeer.getEthPeer(), blockchain.getGenesisBlock().getHeader()); verify(inSyncListener).onInSyncStatusChange(false); verify(inSyncListenerExact).onInSyncStatusChange(false); } + @Test + public void inSyncCheckDrivenByABlockImportDoesNotTakeTheSyncStateMonitor() { + // AERE D-426: upstream hyperledger/besu#11041, ported (unnamed lambda parameter spelled out for Java 21) + final List heldMonitorDuringCallback = new ArrayList<>(); + syncState.subscribeInSync( + ignored -> heldMonitorDuringCallback.add(Thread.holdsLock(syncState)), + Synchronizer.DEFAULT_IN_SYNC_TOLERANCE); + + // Fires the block-added observer and therefore calls checkInSync() on this thread. + advanceLocalChain(blockchain.getChainHeadBlockNumber() + 1); + + assertThat(heldMonitorDuringCallback) + .withFailMessage("a block import called checkInSync() while holding the sync state monitor") + .isNotEmpty() + .containsOnly(false); + } + + /** + * AERE D-426: the chain that froze validator 2 of testnet 28001 for 3 h 18 min on 2026-09-28. The chain downloader + * sets a sync target (replaceSyncTarget holds this monitor) and publishes the sync status; the BFT mining + * coordinator's listener stops the coordinator and waits for the BFT thread, which is importing a block, and the + * block-added observer calls checkInSync(). With checkInSync() on the monitor the import cannot finish while the + * listener waits, so each side waits for the other. + */ + @Test + public void syncStatusListenerWaitingOnABlockImportDoesNotDeadlockTheImport() throws Exception { + final CountDownLatch inCallback = new CountDownLatch(1); + final CountDownLatch blockAppended = new CountDownLatch(1); + final AtomicBoolean importFinishedWhileListenerWaited = new AtomicBoolean(); + final AtomicReference importFailure = new AtomicReference<>(); + syncState.subscribeSyncStatus( + ignored -> { + if (inCallback.getCount() == 0) { + return; + } + inCallback.countDown(); + try { + // stands for BftMiningCoordinator.stop() -> BftProcessor.awaitStop() + importFinishedWhileListenerWaited.set(blockAppended.await(5, TimeUnit.SECONDS)); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + final long headBefore = blockchain.getChainHeadBlockNumber(); + final Thread importer = + new Thread( + () -> { + try { + if (!inCallback.await(5, TimeUnit.SECONDS)) { + return; + } + // stands for QbftRound.importSealedBlock on the BFT thread + advanceLocalChain(headBefore + 1); + blockAppended.countDown(); + } catch (final Throwable t) { + importFailure.set(t); + } + }, + "aere-d426-importer"); + importer.start(); + + // publishes on this thread, as PipelineChainDownloader.startDownloadForSyncTarget does + syncState.setSyncTarget(syncTargetPeer.getEthPeer(), blockchain.getGenesisBlock().getHeader()); + importer.join(TimeUnit.SECONDS.toMillis(30)); + + assertThat(importer.isAlive()).withFailMessage("the importer thread never finished").isFalse(); + assertThat(importFailure.get()).withFailMessage("the import itself failed: %s", importFailure.get()).isNull(); + assertThat(inCallback.getCount()).withFailMessage("the sync status was never published").isZero(); + assertThat(blockchain.getChainHeadBlockNumber()).isEqualTo(headBefore + 1); + assertThat(importFinishedWhileListenerWaited) + .withFailMessage( + "a block import could not finish while a sync-status listener waited for it: " + + "checkInSync() is taking the monitor that replaceSyncTarget() holds (D-426)") + .isTrue(); + } + private void advanceLocalChain(final long newChainHeight) { while (blockchain.getChainHeadBlockNumber() < newChainHeight) { final BlockHeader parent = blockchain.getChainHeadHeader(); final Block block = gen.block( BlockOptions.create() .setDifficulty(standardDifficultyPerBlock) .setParentHash(parent.getHash()) .setBlockNumber(parent.getNumber() + 1L) .transactionCount(0)); final List receipts = gen.receipts(block); blockchain.appendBlock(block, receipts); } } /** * Updates the chain state, such that the peer will end up with an estimated height of {@code * blockHeight} and an estimated total difficulty of {@code totalDifficulty} * * @param peer The peer whose chain should be updated diff --git a/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java b/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java old mode 100644 new mode 100755 index 15fc61698b..695f088181 --- a/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java +++ b/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java @@ -1,33 +1,39 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-bft-bounded-message-lists.patch. */ package org.hyperledger.besu.ethereum.rlp; import java.math.BigInteger; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes48; import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt64; /** * An input used to decode data in RLP encoding. * *

    An RLP "value" is fundamentally an {@code Item} defined the following way: * @@ -369,41 +375,61 @@ public interface RLPInput { /** Resets this RLP input to the start. */ void reset(); /** * Returns a raw {@link Bytes} representation of this RLP list. * * @return The raw RLP list. */ Bytes currentListAsBytes(); /** * Reads a full list from the input given a method that knows how to read its elements. * * @param valueReader A method that can decode a single list element. * @param The type of the elements of the decoded list. * @return The next list of this input, where elements are decoded using {@code valueReader}. * @throws RLPException is the next item to read is not a list, of if any error happens when * applying {@code valueReader} to read elements of the list. */ default List readList(final Function valueReader) { + return readList(valueReader, Integer.MAX_VALUE); + } + + /** + * Reads a full list from the input, rejecting inputs whose element count exceeds {@code + * maxElements}. AERE D-434: ported from upstream hyperledger/besu dd4101c9ec. + * + * @param valueReader A method that can decode a single list element. + * @param maxElements The maximum number of elements the list may contain. + * @param The type of the elements of the decoded list. + * @return The next list of this input, where elements are decoded using {@code valueReader}. + * @throws RLPException if the next item is not a list, if it holds more than {@code maxElements} + * elements, or if any error happens when applying {@code valueReader} to read elements. + */ + default List readList(final Function valueReader, final int maxElements) { final int size = enterList(); + if (size > maxElements) { + throw new RLPException( + String.format( + "List of %d elements exceeds the maximum permitted size of %d", size, maxElements)); + } final List res = size == 0 ? List.of() : new ArrayList<>(size); for (int i = 0; i < size; i++) { try { res.add(valueReader.apply(this)); } catch (final RLPException e) { throw e; } catch (final Exception e) { throw new RLPException( String.format("Error applying element decoding function on element %d of the list", i), e); } } leaveList(); return res; } default Bytes currentListAsBytesNoCopy(final boolean moveToNextItem) { throw new UnsupportedOperationException("Not supported by this input"); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 954f4e1e95..eadf4b3c49 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -1,57 +1,63 @@ /* * Copyright ConsenSys AG. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-warm-access-hashdos-treeset.patch. */ package org.hyperledger.besu.evm.frame; import static com.google.common.base.Preconditions.checkState; import static java.util.Collections.emptySet; import org.hyperledger.besu.collections.undo.UndoSet; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Log; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.internal.MemoryEntry; import org.hyperledger.besu.evm.internal.OperandStack; import org.hyperledger.besu.evm.internal.StorageEntry; import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; -import java.util.HashSet; +import java.util.TreeSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Table; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.MutableBytes; import org.apache.tuweni.units.bigints.UInt256; /** * A container object for all the states associated with a message. * *

    A message corresponds to an interaction between two accounts. A Transaction spawns at least * one message when it's processed. Messages can also spawn messages depending on the code executed * within a message. @@ -1842,41 +1848,41 @@ public class MessageFrame { checkState(sender != null, "Missing message frame sender"); checkState(value != null, "Missing message frame value"); checkState(apparentValue != null, "Missing message frame apparent value"); checkState(code != null, "Missing message frame code"); checkState(completer != null, "Missing message frame completer"); } /** * Build MessageFrame. * * @return instance of MessageFrame */ public MessageFrame build() { validate(); WorldUpdater updater; boolean newStatic; TxValues newTxValues; if (parentMessageFrame == null) { - HashSet

    warmedUpAddresses = new HashSet<>(); + TreeSet
    warmedUpAddresses = new TreeSet<>(); // AERE D-434 (adfa98d013): TreeSet defeats the grindable base-31 HashDoS warmedUpAddresses.add(contract); newTxValues = TxValues.forTransaction( blockHashLookup, maxStackSize, UndoSet.of(warmedUpAddresses), originator, gasPrice, blobGasPrice, blockValues, miningBeneficiary, versionedHashes); updater = worldUpdater; newStatic = isStatic; } else { newTxValues = parentMessageFrame.txValues; updater = parentMessageFrame.getWorldUpdater().updater(); newStatic = isStatic || parentMessageFrame.isStatic; parentMessageFrame.warmUpAddress(contract); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java index d4c9edad39..b13a96f5bb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java @@ -1,51 +1,57 @@ /* * Copyright contributors to Hyperledger Besu. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-warm-access-hashdos-treeset.patch. */ package org.hyperledger.besu.evm.frame; import org.hyperledger.besu.collections.undo.UndoScalar; import org.hyperledger.besu.collections.undo.UndoSet; import org.hyperledger.besu.collections.undo.UndoTable; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import java.util.ArrayDeque; import java.util.Deque; -import java.util.HashSet; +import java.util.TreeSet; import java.util.List; import java.util.Optional; -import com.google.common.collect.HashBasedTable; +import com.google.common.collect.TreeBasedTable; import org.apache.tuweni.bytes.Bytes32; /** * Transaction Values used by various EVM Opcodes. These are the values that either do not change or * the backing stores whose changes transcend message frames and are not part of state, such as * transient storage and address warming. * * @param blockHashLookup The block hash lookup function * @param maxStackSize The maximum stack size * @param warmedUpAddresses The warmed-up addresses * @param warmedUpStorage The warmed-up storage * @param originator The originator address * @param gasPrice The gas price * @param blobGasPrice The blob gas price * @param blockValues The block values * @param messageFrameStack The message frame stack * @param miningBeneficiary The mining beneficiary address * @param versionedHashes The optional list of versioned hashes * @param transientStorage The transient storage * @param creates The set of addresses that creates @@ -93,51 +99,51 @@ public record TxValues( * @param blobGasPrice the blob gas price * @param blockValues the block values * @param miningBeneficiary the mining beneficiary * @param versionedHashes optional versioned hashes * @return a new TxValues instance */ public static TxValues forTransaction( final BlockHashLookup blockHashLookup, final int maxStackSize, final UndoSet
    warmedUpAddresses, final Address originator, final Wei gasPrice, final Wei blobGasPrice, final BlockValues blockValues, final Address miningBeneficiary, final Optional> versionedHashes) { return new TxValues( blockHashLookup, maxStackSize, warmedUpAddresses, - UndoTable.of(HashBasedTable.create()), + UndoTable.of(TreeBasedTable.create()), originator, gasPrice, blobGasPrice, blockValues, new ArrayDeque<>(), miningBeneficiary, versionedHashes, - UndoTable.of(HashBasedTable.create()), - UndoSet.of(new HashSet<>()), - UndoSet.of(new HashSet<>()), + UndoTable.of(TreeBasedTable.create()), + UndoSet.of(new TreeSet<>()), + UndoSet.of(new TreeSet<>()), new UndoScalar<>(0L), new UndoScalar<>(0L), new UndoScalar<>(0L), new long[] {0L}, new long[] {0L}); } /** * For all data stored in this record, undo the changes since the mark. * * @param mark the mark to which it should be rolled back to */ public void undoChanges(final long mark) { warmedUpAddresses.undo(mark); warmedUpStorage.undo(mark); transientStorage.undo(mark); creates.undo(mark); selfDestructs.undo(mark); gasRefunds.undo(mark); stateGasUsed.undo(mark); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/P256VerifyPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/P256VerifyPrecompiledContract.java index 47b7145d17..220194b6ab 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/P256VerifyPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/P256VerifyPrecompiledContract.java @@ -1,33 +1,39 @@ /* * Copyright contributors to Besu. * * 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 + * + * Modifications Copyright contributors to the Aere Network. + * + * This file was modified by contributors to the Aere Network, as required by section 4(b) of the + * Apache License 2.0. The copyright header above is the upstream one and is left exactly as it was + * found, as section 4(c) requires. The change itself is in patches/aere-drop-per-request-console-writes.patch. */ package org.hyperledger.besu.evm.precompile; import org.hyperledger.besu.crypto.SECP256R1; import org.hyperledger.besu.crypto.SECPPublicKey; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.nativelib.boringssl.BoringSSLPrecompiles; import java.math.BigInteger; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import jakarta.validation.constraints.NotNull; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -152,41 +158,41 @@ public class P256VerifyPrecompiledContract extends AbstractPrecompiledContract { } } else { cacheEventConsumer.accept(new CacheEvent(PRECOMPILE_NAME, CacheMetric.MISS)); } } try { if (useNativeBoringSSL) { res = computeNative(input); } else { res = computeDefault(input); } if (enableResultCaching) { p256VerifyCache.put(cacheKey, res); } return res.cachedResult(); } catch (Exception e) { LOG.warn("P256VERIFY verification failed: {}", e.getMessage()); - System.err.println("P256VERIFY verification failed: " + e.getMessage()); + // AERE D-434 (fbfb5f9ecd): removed System.err print on the P256VERIFY failure path; a cheap invalid signature could otherwise spam the process console, bypassing log4j. LOG.warn above records it. return PrecompileContractResult.success(INVALID); } } @NotNull private static PrecompileInputResultTuple computeNative(final Bytes input) { PrecompileInputResultTuple res; BoringSSLPrecompiles.P256VerifyResult result = BoringSSLPrecompiles.p256Verify(input.toArrayUnsafe(), input.size()); if (result.status != 0) { LOG.atTrace().setMessage("Verify failed: {}").addArgument(result.error).log(); } res = new PrecompileInputResultTuple( enableResultCaching ? input.copy() : input, PrecompileContractResult.success(result.status == 0 ? VALID : INVALID)); return res; }