def test_randao_reveal_validation(is_valid, epoch, expected_epoch, proposer_key_index, expected_proposer_key_index, privkeys, pubkeys, sample_fork_params, config): message_hash = epoch.to_bytes(32, byteorder="little") slot = epoch * config.SLOTS_PER_EPOCH fork = Fork(**sample_fork_params) domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO) proposer_privkey = privkeys[proposer_key_index] randao_reveal = bls.sign( message_hash=message_hash, privkey=proposer_privkey, domain=domain, ) expected_proposer_pubkey = pubkeys[expected_proposer_key_index] try: validate_randao_reveal( randao_reveal=randao_reveal, proposer_pubkey=expected_proposer_pubkey, epoch=expected_epoch, fork=fork, ) except ValidationError: if is_valid: raise else: if not is_valid: pytest.fail("Did not raise")
def sign_proof_of_possession(deposit_input, privkey, fork_data, slot): domain = get_domain( fork_data, slot, SignatureDomain.DOMAIN_DEPOSIT, ) return bls.sign(deposit_input.root, privkey, domain)
def create_block_on_state( *, state: BeaconState, config: BeaconConfig, state_machine: BaseBeaconStateMachine, block_class: BaseBeaconBlock, parent_block: BaseBeaconBlock, slot: SlotNumber, validator_index: ValidatorIndex, privkey: int, attestations: Sequence[Attestation], check_proposer_index: bool = True) -> BaseBeaconBlock: """ Create a beacon block with the given parameters. """ # Check proposer if check_proposer_index: validate_proposer_index(state, config, slot, validator_index) # Prepare block: slot and parent_root block = block_class.from_parent( parent_block=parent_block, block_params=FromBlockParams(slot=slot), ) # TODO: Add more operations randao_reveal = ZERO_HASH32 eth1_data = Eth1Data.create_empty_data() body = BeaconBlockBody.create_empty_body().copy( attestations=attestations, ) block = block.copy( randao_reveal=randao_reveal, eth1_data=eth1_data, body=body, ) # Apply state transition to get state root state, block = state_machine.import_block(block, check_proposer_signature=True) # Sign empty_signature_block_root = block.block_without_signature_root proposal_root = ProposalSignedData( slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, ).root domain = get_domain( state.fork, slot_to_epoch(slot, config.EPOCH_LENGTH), SignatureDomain.DOMAIN_PROPOSAL, ) block = block.copy(signature=bls.sign( message=proposal_root, privkey=privkey, domain=domain, ), ) return block
def test_randao_processing(sample_beacon_block_params, sample_beacon_state_params, sample_fork_params, keymap, config): proposer_pubkey, proposer_privkey = first(keymap.items()) state = SerenityBeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey) for _ in range(config.TARGET_COMMITTEE_SIZE)), validator_balances=(config.MAX_DEPOSIT_AMOUNT, ) * config.TARGET_COMMITTEE_SIZE, latest_randao_mixes=tuple( ZERO_HASH32 for _ in range(config.LATEST_RANDAO_MIXES_LENGTH)), ) epoch = state.current_epoch(config.EPOCH_LENGTH) slot = epoch * config.EPOCH_LENGTH message = epoch.to_bytes(32, byteorder="big") fork = Fork(**sample_fork_params) domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO) randao_reveal = bls.sign(message, proposer_privkey, domain) block = SerenityBeaconBlock(**sample_beacon_block_params).copy( randao_reveal=randao_reveal, ) new_state = process_randao(state, block, config) updated_index = epoch % config.LATEST_RANDAO_MIXES_LENGTH original_mixes = state.latest_randao_mixes updated_mixes = new_state.latest_randao_mixes assert all( updated == original if index != updated_index else updated != original for index, (updated, original) in enumerate(zip(updated_mixes, original_mixes)))
def test_signature_aggregation(msg, privkeys): domain = 0 sigs = [sign(msg, k, domain=domain) for k in privkeys] pubs = [privtopub(k) for k in privkeys] aggsig = aggregate_signatures(sigs) aggpub = aggregate_pubkeys(pubs) assert verify(msg, aggpub, aggsig, domain=domain)
def test_randao_processing_validates_randao_reveal(sample_beacon_block_params, sample_beacon_state_params, sample_fork_params, keymap, config): proposer_pubkey, proposer_privkey = first(keymap.items()) state = SerenityBeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey) for _ in range(config.TARGET_COMMITTEE_SIZE)), validator_balances=(config.MAX_DEPOSIT_AMOUNT, ) * config.TARGET_COMMITTEE_SIZE, latest_randao_mixes=tuple( ZERO_HASH32 for _ in range(config.LATEST_RANDAO_MIXES_LENGTH)), ) epoch = state.current_epoch(config.EPOCH_LENGTH) slot = epoch * config.EPOCH_LENGTH message = (epoch + 1).to_bytes(32, byteorder="big") fork = Fork(**sample_fork_params) domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO) randao_reveal = bls.sign(message, proposer_privkey, domain) block = SerenityBeaconBlock(**sample_beacon_block_params).copy( randao_reveal=randao_reveal, ) with pytest.raises(ValidationError): process_randao(state, block, config)
def create_block_on_state(state: BeaconState, config: BeaconConfig, block_class: BaseBeaconBlock, parent_block: BaseBeaconBlock, slot: SlotNumber, validator_index: int, privkey: int, attestations: Sequence[Attestation]): """ Create a beacon block with the given parameters. """ # Check proposer beacon_proposer_index = get_beacon_proposer_index( state.copy(slot=slot, ), slot, config.EPOCH_LENGTH, config.TARGET_COMMITTEE_SIZE, config.SHARD_COUNT, ) if validator_index != beacon_proposer_index: raise ProposerIndexError # Prepare block: slot and parent_root block = block_class.from_parent( parent_block=parent_block, block_params=FromBlockParams(slot=slot), ) # TODO: Add more operations randao_reveal = ZERO_HASH32 eth1_data = Eth1Data.create_empty_data() body = BeaconBlockBody.create_empty_body().copy( attestations=attestations, ) block = block.copy( randao_reveal=randao_reveal, eth1_data=eth1_data, body=body, ) # Sign empty_signature_block_root = block.block_without_signature_root proposal_root = ProposalSignedData( slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, ).root domain = get_domain( state.fork, slot, SignatureDomain.DOMAIN_PROPOSAL, ) block = block.copy(signature=bls.sign( message=proposal_root, privkey=privkey, domain=domain, ), ) return block
def sign_attestation(message: bytes, privkey: int, fork: Fork, slot: SlotNumber, epoch_length: int) -> BLSSignature: domain = get_domain( fork, slot_to_epoch(slot, epoch_length), SignatureDomain.DOMAIN_ATTESTATION, ) return bls.sign( message=message, privkey=privkey, domain=domain, )
def sign_proof_of_possession(deposit_input: DepositInput, privkey: int, fork: Fork, slot: SlotNumber) -> BLSSignature: domain = get_domain( fork, slot, SignatureDomain.DOMAIN_DEPOSIT, ) return bls.sign( message=deposit_input.root, privkey=privkey, domain=domain, )
def _corrupt_signature(params, fork_data): message = bytes.fromhex("deadbeefcafe") privkey = 42 domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( fork_data=fork_data, slot=params["data"].slot, domain_type=domain_type, ) corrupt_signature = bls.sign(message, privkey, domain) return assoc(params, "aggregate_signature", corrupt_signature)
def _corrupt_signature(slots_per_epoch, params, fork): message_hash = b'\x12' * 32 privkey = 42 domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( fork=fork, epoch=slot_to_epoch(params["data"].slot, slots_per_epoch), domain_type=domain_type, ) corrupt_signature = bls.sign(message_hash, privkey, domain) return assoc(params, "aggregate_signature", corrupt_signature)
def _corrupt_signature(epoch_length, params, fork): message = bytes.fromhex("deadbeefcafe") privkey = 42 domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( fork=fork, epoch=slot_to_epoch(params["data"].slot, epoch_length), domain_type=domain_type, ) corrupt_signature = bls.sign(message, privkey, domain) return assoc(params, "aggregate_signature", corrupt_signature)
def test_bls_core(privkey): domain = 0 p1 = multiply(G1, privkey) p2 = multiply(G2, privkey) msg = str(privkey).encode('utf-8') msghash = hash_to_G2(msg, domain=domain) assert normalize(decompress_G1(compress_G1(p1))) == normalize(p1) assert normalize(decompress_G2(compress_G2(p2))) == normalize(p2) assert normalize(decompress_G2(compress_G2(msghash))) == normalize(msghash) sig = sign(msg, privkey, domain=domain) pub = privtopub(privkey) assert verify(msg, pub, sig, domain=domain)
def sign_transaction(*, message_hash: Hash32, privkey: int, fork: Fork, slot: Slot, signature_domain: SignatureDomain, slots_per_epoch: int) -> BLSSignature: domain = get_domain( fork, slot_to_epoch(slot, slots_per_epoch), signature_domain, ) return bls.sign( message_hash=message_hash, privkey=privkey, domain=domain, )
def sign_proof_of_possession(deposit_input: DepositInput, privkey: int, fork: Fork, slot: Slot, slots_per_epoch: int) -> BLSSignature: domain = get_domain( fork, slot_to_epoch(slot, slots_per_epoch), SignatureDomain.DOMAIN_DEPOSIT, ) return bls.sign( message_hash=deposit_input.root, privkey=privkey, domain=domain, )
def _get_indices_and_signatures(num_validators, message, privkeys, fork, slot): num_indices = 5 assert num_validators >= num_indices indices = random.sample(range(num_validators), num_indices) privkeys = [privkeys[i] for i in indices] domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( fork=fork, slot=slot, domain_type=domain_type, ) signatures = tuple( map(lambda key: bls.sign(message, key, domain), privkeys)) return (indices, signatures)
def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2): domain = 0 sigs_1 = [sign(msg_1, k, domain=domain) for k in privkeys_1] pubs_1 = [privtopub(k) for k in privkeys_1] aggsig_1 = aggregate_signatures(sigs_1) aggpub_1 = aggregate_pubkeys(pubs_1) sigs_2 = [sign(msg_2, k, domain=domain) for k in privkeys_2] pubs_2 = [privtopub(k) for k in privkeys_2] aggsig_2 = aggregate_signatures(sigs_2) aggpub_2 = aggregate_pubkeys(pubs_2) msgs = [msg_1, msg_2] pubs = [aggpub_1, aggpub_2] aggsig = aggregate_signatures([aggsig_1, aggsig_2]) assert verify_multiple( pubkeys=pubs, messages=msgs, signature=aggsig, domain=domain, )
def test_validate_serenity_proposer_signature( proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, sample_shard_committee_params, beacon_chain_shard_number, epoch_length, max_deposit): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey) for _ in range(10)), validator_balances=(max_deposit * GWEI_PER_ETH, ) * 10, shard_committees_at_slots=get_sample_shard_committees_at_slots( num_slot=128, num_shard_committee_per_slot=10, sample_shard_committee_params=sample_shard_committee_params, ), ) default_block = BeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root proposal_root = ProposalSignedData( state.slot, beacon_chain_shard_number, empty_signature_block_root, ).root proposed_block = BeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( message=proposal_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) if is_valid_signature: validate_serenity_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, ) else: with pytest.raises(ValidationError): validate_serenity_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, )
def test_validate_proposer_signature( slots_per_epoch, shard_count, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, beacon_chain_shard_number, genesis_epoch, target_committee_size, max_deposit_amount, config): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(proposer_pubkey, config) for _ in range(10)), validator_balances=(max_deposit_amount, ) * 10, ) default_block = BeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root proposal_signed_root = Proposal( state.slot, beacon_chain_shard_number, empty_signature_block_root, ).signed_root proposed_block = BeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( message_hash=proposal_signed_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, CommitteeConfig(config), ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, CommitteeConfig(config), )
def test_demo(base_db, sample_beacon_block_params, genesis_state, fixture_sm_class, config, privkeys, pubkeys): chaindb = BeaconChainDB(base_db) state = genesis_state block = SerenityBeaconBlock(**sample_beacon_block_params).copy( slot=state.slot + 2, state_root=state.root, ) # Sign block beacon_proposer_index = get_beacon_proposer_index( state, block.slot, config.EPOCH_LENGTH, ) index_in_privkeys = pubkeys.index( state.validator_registry[beacon_proposer_index].pubkey) beacon_proposer_privkey = privkeys[index_in_privkeys] empty_signature_block_root = block.block_without_signature_root proposal_root = ProposalSignedData( block.slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, ).root block = block.copy(signature=bls.sign( message=proposal_root, privkey=beacon_proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) # Store in chaindb chaindb.persist_block(block, SerenityBeaconBlock) chaindb.persist_state(state) # Get state machine instance sm = fixture_sm_class(chaindb, block.root, SerenityBeaconBlock) result_state, _ = sm.import_block(block) assert state.slot == 0 assert result_state.slot == block.slot assert isinstance(sm.block, SerenityBeaconBlock)
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () domain = 0 random_votes = random.sample(range(bit_count), votes_count) message_hash = b'\x12' * 32 # Get votes: (committee_index, sig, public_key) votes = [( committee_index, bls.sign(message_hash, privkeys[committee_index], domain), pubkeys[committee_index], ) for committee_index in random_votes] # Verify sigs, committee_indices = verify_votes(message_hash, votes, domain) # Aggregate the votes bitfield, sigs = aggregate_votes( bitfield=pre_bitfield, sigs=pre_sigs, voting_sigs=sigs, voting_committee_indices=committee_indices) try: _, _, pubs = zip(*votes) except ValueError: pubs = () voted_index = [ committee_index for committee_index in random_votes if has_voted(bitfield, committee_index) ] assert len(voted_index) == len(votes) aggregated_pubs = bls.aggregate_pubkeys(pubs) assert bls.verify(message_hash, aggregated_pubs, sigs, domain)
def create_mock_signed_attestation(state, shard_committee, voting_committee_indices, attestation_data): message = hash_eth2( rlp.encode(attestation_data) + (0).to_bytes(1, "big") ) # participants sign message signatures = [ bls.sign( message, privkeys[shard_committee.committee[committee_index]], domain=get_domain( fork_data=state.fork_data, slot=attestation_data.slot, domain_type=SignatureDomain.DOMAIN_ATTESTATION, ) ) for committee_index in voting_committee_indices ] # aggregate signatures and construct participant bitfield participation_bitfield, aggregate_signature = aggregate_votes( bitfield=get_empty_bitfield(len(shard_committee.committee)), sigs=(), voting_sigs=signatures, voting_committee_indices=voting_committee_indices, ) # create attestation from attestation_data, particpipant_bitfield, and signature return Attestation( data=attestation_data, participation_bitfield=participation_bitfield, custody_bitfield=b'', aggregate_signature=aggregate_signature, )