# beacon_chain # Copyright (c) 2018-2021 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. import random, chronicles, options, stew/endians2, ../beacon_chain/consensus_object_pools/sync_committee_msg_pool, ../beacon_chain/validators/validator_pool, ../beacon_chain/spec/datatypes/merge, ../beacon_chain/spec/[ beaconstate, helpers, keystore, signatures, state_transition, validator] type MockPrivKeysT = object MockPubKeysT = object const MockPrivKeys* = MockPrivKeysT() MockPubKeys* = MockPubKeysT() # https://github.com/ethereum/consensus-specs/blob/v1.1.5/tests/core/pyspec/eth2spec/test/helpers/keys.py func `[]`*(_: MockPrivKeysT, index: ValidatorIndex): ValidatorPrivKey = # 0 is not a valid BLS private key - 1000 helps interop with rust BLS library, # lighthouse. EF tests use 1 instead of 1000. var bytes = (index.uint64 + 1000'u64).toBytesLE() static: doAssert sizeof(bytes) <= sizeof(result) copyMem(addr result, addr bytes, sizeof(bytes)) func `[]`*(_: MockPubKeysT, index: ValidatorIndex): ValidatorPubKey = MockPrivKeys[index].toPubKey().toPubKey() func makeFakeHash*(i: int): Eth2Digest = var bytes = uint64(i).toBytesLE() static: doAssert sizeof(bytes) <= sizeof(result.data) copyMem(addr result.data[0], addr bytes[0], sizeof(bytes)) func makeDeposit*( i: int, flags: UpdateFlags = {}, cfg = defaultRuntimeConfig): DepositData = let privkey = MockPrivKeys[i.ValidatorIndex] pubkey = MockPubKeys[i.ValidatorIndex] withdrawal_credentials = makeWithdrawalCredentials(pubkey) result = DepositData( pubkey: pubkey, withdrawal_credentials: withdrawal_credentials, amount: MAX_EFFECTIVE_BALANCE) if skipBLSValidation notin flags: result.signature = get_deposit_signature(cfg, result, privkey).toValidatorSig() func makeInitialDeposits*( n = SLOTS_PER_EPOCH, flags: UpdateFlags = {}, cfg = defaultRuntimeConfig): seq[DepositData] = for i in 0.. committee index -> # montonoic enumerable index, is wasteful and slow. Most test callers # want ValidatorIndex, so that's supported too. let sac_index = committee.find(validator_index) data = makeAttestationData(state, slot, index, beacon_block_root) doAssert sac_index != -1, "find_beacon_committee should guarantee this" var aggregation_bits = CommitteeValidatorsBits.init(committee.len) aggregation_bits.setBit sac_index let sig = if skipBLSValidation notin flags: get_attestation_signature( getStateField(state, fork), getStateField(state, genesis_validators_root), data, MockPrivKeys[validator_index]).toValidatorSig() else: ValidatorSig() Attestation( data: data, aggregation_bits: aggregation_bits, signature: sig ) func find_beacon_committee( state: ForkedHashedBeaconState, validator_index: ValidatorIndex, cache: var StateCache): auto = let epoch = compute_epoch_at_slot(getStateField(state, slot)) for epoch_committee_index in 0'u64 ..< get_committee_count_per_slot( state, epoch, cache) * SLOTS_PER_EPOCH: let slot = ((epoch_committee_index mod SLOTS_PER_EPOCH) + epoch.compute_start_slot_at_epoch.uint64).Slot index = CommitteeIndex(epoch_committee_index div SLOTS_PER_EPOCH) committee = get_beacon_committee(state, slot, index, cache) if validator_index in committee: return (committee, slot, index) doAssert false func makeAttestation*( state: ForkedHashedBeaconState, beacon_block_root: Eth2Digest, validator_index: ValidatorIndex, cache: var StateCache): Attestation = let (committee, slot, index) = find_beacon_committee(state, validator_index, cache) makeAttestation(state, beacon_block_root, committee, slot, index, validator_index, cache) func makeFullAttestations*( state: ForkedHashedBeaconState, beacon_block_root: Eth2Digest, slot: Slot, cache: var StateCache, flags: UpdateFlags = {}): seq[Attestation] = # Create attestations in which the full committee participates for each shard # that should be attested to during a particular slot let committees_per_slot = get_committee_count_per_slot(state, slot.epoch, cache) for index in 0'u64..= 1 # Initial attestation var attestation = Attestation( aggregation_bits: CommitteeValidatorsBits.init(committee.len), data: data) var agg {.noInit.}: AggregateSignature agg.init(get_attestation_signature( getStateField(state, fork), getStateField(state, genesis_validators_root), data, MockPrivKeys[committee[0]])) # Aggregate the remainder attestation.aggregation_bits.setBit 0 for j in 1 ..< committee.len(): attestation.aggregation_bits.setBit j if skipBLSValidation notin flags: agg.aggregate(get_attestation_signature( getStateField(state, fork), getStateField(state, genesis_validators_root), data, MockPrivKeys[committee[j]] )) attestation.signature = agg.finish().toValidatorSig() result.add attestation proc makeSyncAggregate( state: ForkedHashedBeaconState, syncCommitteeRatio: float, cfg: RuntimeConfig): SyncAggregate = if syncCommitteeRatio <= 0.0: return SyncAggregate.init() let syncCommittee = withState(state): when stateFork >= BeaconStateFork.Altair: const SLOTS_PER_PERIOD = EPOCHS_PER_SYNC_COMMITTEE_PERIOD * SLOTS_PER_EPOCH if (state.data.slot + 1) mod SLOTS_PER_PERIOD == 0: state.data.next_sync_committee else: state.data.current_sync_committee else: return SyncAggregate.init() fork = getStateField(state, fork) genesis_validators_root = getStateField(state, genesis_validators_root) slot = getStateField(state, slot) latest_block_root = withState(state): state.latest_block_root syncCommitteePool = newClone(SyncCommitteeMsgPool.init()) type Aggregator = object subcommitteeIdx: SyncSubcommitteeIndex validatorIdx: ValidatorIndex selectionProof: ValidatorSig var aggregators: seq[Aggregator] for subcommitteeIdx in allSyncSubcommittees(): let firstKeyIdx = subcommitteeIdx.int * SYNC_SUBCOMMITTEE_SIZE lastKeyIdx = firstKeyIdx + SYNC_SUBCOMMITTEE_SIZE - 1 var processedKeys = initHashSet[ValidatorPubKey]() for idx, validatorKey in syncCommittee.pubkeys[firstKeyIdx .. lastKeyIdx]: if validatorKey in processedKeys: continue processedKeys.incl validatorKey if rand(1.0) > syncCommitteeRatio: continue var positions: seq[uint64] for pos, key in syncCommittee.pubkeys[firstKeyIdx + idx .. lastKeyIdx]: if key == validatorKey: positions.add (idx + pos).uint64 let validatorIdx = block: var res = 0 for i, validator in getStateField(state, validators): if validator.pubkey == validatorKey: res = i break res.ValidatorIndex signature = get_sync_committee_message_signature( fork, genesis_validators_root, slot, latest_block_root, MockPrivKeys[validatorIdx]) selectionProofSig = get_sync_committee_selection_proof( fork, genesis_validators_root, slot, subcommitteeIdx.uint64, MockPrivKeys[validatorIdx]) syncCommitteePool[].addSyncCommitteeMessage( slot, latest_block_root, uint64 validatorIdx, signature, subcommitteeIdx, positions) if is_sync_committee_aggregator(selectionProofSig.toValidatorSig): aggregators.add Aggregator( subcommitteeIdx: subcommitteeIdx, validatorIdx: validatorIdx, selectionProof: selectionProofSig.toValidatorSig) for aggregator in aggregators: var contribution: SyncCommitteeContribution if syncCommitteePool[].produceContribution( slot, latest_block_root, aggregator.subcommitteeIdx, contribution): let contributionAndProof = ContributionAndProof( aggregator_index: uint64 aggregator.validatorIdx, contribution: contribution, selection_proof: aggregator.selectionProof) contributionSig = get_contribution_and_proof_signature( fork, genesis_validators_root, contributionAndProof, MockPrivKeys[aggregator.validatorIdx]) signedContributionAndProof = SignedContributionAndProof( message: contributionAndProof, signature: contributionSig.toValidatorSig) syncCommitteePool[].addContribution( signedContributionAndProof, contribution.signature.load.get) syncCommitteePool[].produceSyncAggregate(latest_block_root) iterator makeTestBlocks*( state: ForkedHashedBeaconState, cache: var StateCache, blocks: int, attested: bool, syncCommitteeRatio = 0.0, cfg = defaultRuntimeConfig): ForkedSignedBeaconBlock = var state = assignClone(state) for _ in 0..