def test_proposer_slashing(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices( test_state.validator_registry, current_epoch)[-1] pubkey = pubkeys[validator_index] privkey = pubkey_to_privkey[pubkey] slot = spec.GENESIS_SLOT header_1 = BeaconBlockHeader(slot=slot, previous_block_root=b'\x00' * 32, state_root=b'\x00' * 32, block_body_root=b'\x00' * 32, signature=b'\x00' * 96) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( fork=test_state.fork, epoch=get_current_epoch(test_state), domain_type=spec.DOMAIN_BEACON_BLOCK, ) header_1.signature = bls.sign( message_hash=signed_root(header_1), privkey=privkey, domain=domain, ) header_2.signature = bls.sign( message_hash=signed_root(header_2), privkey=privkey, domain=domain, ) proposer_slashing = ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, ) # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.body.proposer_slashings.append(proposer_slashing) state_transition(test_state, block) assert not state.validator_registry[validator_index].initiated_exit assert not state.validator_registry[validator_index].slashed slashed_validator = test_state.validator_registry[validator_index] assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH # lost whistleblower reward assert test_state.validator_balances[ validator_index] < state.validator_balances[validator_index] return state, [block], test_state
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, config) 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.SLOTS_PER_EPOCH) slot = epoch * config.SLOTS_PER_EPOCH message_hash = (epoch + 1).to_bytes(32, byteorder="little") fork = Fork(**sample_fork_params) domain = get_domain(fork, slot, SignatureDomain.DOMAIN_RANDAO) randao_reveal = bls.sign(message_hash, proposer_privkey, domain) block = SerenityBeaconBlock(**sample_beacon_block_params).copy( randao_reveal=randao_reveal, ) with pytest.raises(ValidationError): process_randao(state, block, config)
def test_signature_aggregation(msg, privkeys): domain = (0).to_bytes(8, "little") 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_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_index=expected_proposer_key_index, 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 case06_aggregate_sigs(): for domain in DOMAINS: for message in MESSAGES: sigs = [bls.sign(message, privkey, domain) for privkey in PRIVKEYS] yield f'agg_sigs_{encode_hex(message)}_{encode_hex(domain)}', { 'input': [encode_hex(sig) for sig in sigs], 'output': encode_hex(bls.aggregate_signatures(sigs)), }
def case06_aggregate_sigs(): for domain in DOMAINS: for message in MESSAGES: sigs = [bls.sign(message, privkey, domain) for privkey in PRIVKEYS] yield { 'input': ['0x' + sig.hex() for sig in sigs], 'output': '0x' + bls.aggregate_signatures(sigs).hex(), }
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 case04_sign_messages(): for privkey in PRIVKEYS: for message in MESSAGES: for domain in DOMAINS: sig = bls.sign(message, privkey, domain) yield { 'input': { 'privkey': int_to_hex(privkey), 'message': '0x' + message.hex(), 'domain': int_to_hex(domain, byte_length=8) }, 'output': '0x' + sig.hex() }
def test_bls_core(privkey): domain = (0).to_bytes(8, "little") 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_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 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 case04_sign_messages(): for privkey in PRIVKEYS: for message in MESSAGES: for domain in DOMAINS: sig = bls.sign(message, privkey, domain) yield f'sign_msg_{int_to_hex(privkey)}_{encode_hex(message)}_{encode_hex(domain)}', { 'input': { 'privkey': int_to_hex(privkey), 'message': encode_hex(message), 'domain': encode_hex(domain), }, 'output': encode_hex(sig) }
def case04_sign_messages(): for privkey in PRIVKEYS: for message in MESSAGES: for domain in DOMAINS: sig = bls.sign(message, privkey, domain) full_name = f'{int_to_hex(privkey)}_{encode_hex(message)}_{encode_hex(domain)}' yield f'sign_msg_case_{(hash(bytes(full_name, "utf-8"))[:8]).hex()}', { 'input': { 'privkey': int_to_hex(privkey), 'message': encode_hex(message), 'domain': encode_hex(domain), }, 'output': encode_hex(sig) }
def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2): domain = (0).to_bytes(8, "little") 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) message_hashes = [msg_1, msg_2] pubs = [aggpub_1, aggpub_2] aggsig = aggregate_signatures([aggsig_1, aggsig_2]) assert verify_multiple( pubkeys=pubs, message_hashes=message_hashes, signature=aggsig, domain=domain, )
def test_transfer(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1] recipient_index = get_active_validator_indices( pre_state.validator_registry, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = pubkey_to_privkey[transfer_pubkey] amount = pre_state.validator_balances[sender_index] pre_transfer_recipient_balance = pre_state.validator_balances[ recipient_index] transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=0, slot=pre_state.slot + 1, pubkey=transfer_pubkey, signature=b'\x00' * 96, ) transfer.signature = bls.sign(message_hash=signed_root(transfer), privkey=transfer_privkey, domain=get_domain( fork=pre_state.fork, epoch=get_current_epoch(pre_state), domain_type=spec.DOMAIN_TRANSFER, )) # ensure withdrawal_credentials reproducable pre_state.validator_registry[sender_index].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer_pubkey)[1:]) # un-activate so validator can transfer pre_state.validator_registry[ sender_index].activation_epoch = spec.FAR_FUTURE_EPOCH post_state = deepcopy(pre_state) # # Add to state via block transition # block = construct_empty_block_for_next_slot(post_state) block.body.transfers.append(transfer) state_transition(post_state, block) sender_balance = post_state.validator_balances[sender_index] recipient_balance = post_state.validator_balances[recipient_index] assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount return pre_state, [block], post_state
def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( pre_state.validator_registry, get_current_epoch(pre_state))[-1] pubkey = pubkeys[validator_index] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH # artificially trigger registry update at next epoch transition pre_state.validator_registry_update_epoch -= 1 post_state = deepcopy(pre_state) voluntary_exit = VoluntaryExit( epoch=get_current_epoch(pre_state), validator_index=validator_index, signature=b'\x00' * 96, ) voluntary_exit.signature = bls.sign( message_hash=signed_root(voluntary_exit), privkey=pubkey_to_privkey[pubkey], domain=get_domain( fork=pre_state.fork, epoch=get_current_epoch(pre_state), domain_type=spec.DOMAIN_VOLUNTARY_EXIT, )) # # Add to state via block transition # initiate_exit_block = construct_empty_block_for_next_slot(post_state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) state_transition(post_state, initiate_exit_block) assert not pre_state.validator_registry[validator_index].initiated_exit assert post_state.validator_registry[validator_index].initiated_exit assert post_state.validator_registry[ validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # # Process within epoch transition # exit_block = construct_empty_block_for_next_slot(post_state) exit_block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, exit_block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return pre_state, [initiate_exit_block, exit_block], post_state
def get_valid_proposer_slashing(state): current_epoch = get_current_epoch(state) validator_index = get_active_validator_indices(state, current_epoch)[-1] privkey = pubkey_to_privkey[ state.validator_registry[validator_index].pubkey] slot = state.slot header_1 = BeaconBlockHeader( slot=slot, previous_block_root=ZERO_HASH, state_root=ZERO_HASH, block_body_root=ZERO_HASH, ) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( state=state, domain_type=spec.DOMAIN_BEACON_PROPOSER, ) header_1.signature = bls.sign( message_hash=signing_root(header_1), privkey=privkey, domain=domain, ) header_2.signature = bls.sign( message_hash=signing_root(header_2), privkey=privkey, domain=domain, ) return ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, )
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0): message_hash = AttestationDataAndCustodyBit( data=attestation_data, custody_bit=custody_bit, ).hash_tree_root() return bls.sign( message_hash=message_hash, privkey=privkey, domain=get_domain( state=state, domain_type=spec.DOMAIN_ATTESTATION, message_epoch=slot_to_epoch(attestation_data.slot), ) )
def build_voluntary_exit(state, epoch, validator_index, privkey): voluntary_exit = VoluntaryExit( epoch=epoch, validator_index=validator_index, ) voluntary_exit.signature = bls.sign( message_hash=signing_root(voluntary_exit), privkey=privkey, domain=get_domain( state=state, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, message_epoch=epoch, )) return voluntary_exit
def test_transfer(state): # overwrite default 0 to test spec.MAX_TRANSFERS = 1 pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state, current_epoch)[-1] recipient_index = get_active_validator_indices(pre_state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] amount = get_balance(pre_state, sender_index) pre_transfer_recipient_balance = get_balance(pre_state, recipient_index) transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=0, slot=pre_state.slot + 1, pubkey=transfer_pubkey, ) transfer.signature = bls.sign(message_hash=signing_root(transfer), privkey=transfer_privkey, domain=get_domain( state=pre_state, domain_type=spec.DOMAIN_TRANSFER, )) # ensure withdrawal_credentials reproducable pre_state.validator_registry[sender_index].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer_pubkey)[1:]) # un-activate so validator can transfer pre_state.validator_registry[ sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH post_state = deepcopy(pre_state) # # Add to state via block transition # block = build_empty_block_for_next_slot(post_state) block.body.transfers.append(transfer) state_transition(post_state, block) sender_balance = get_balance(post_state, sender_index) recipient_balance = get_balance(post_state, recipient_index) assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount return pre_state, [block], post_state
def build_voluntary_exit(state, epoch, validator_index, privkey): voluntary_exit = VoluntaryExit( epoch=epoch, validator_index=validator_index, signature=EMPTY_SIGNATURE, ) voluntary_exit.signature = bls.sign( message_hash=signed_root(voluntary_exit), privkey=privkey, domain=get_domain( fork=state.fork, epoch=epoch, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, )) return voluntary_exit
def build_deposit_data(state, pubkey, privkey, amount): deposit_data = DepositData( pubkey=pubkey, # insecurely use pubkey as withdrawal key as well withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:], amount=amount, ) signature = bls.sign(message_hash=signing_root(deposit_data), privkey=privkey, domain=get_domain( state, spec.DOMAIN_DEPOSIT, )) deposit_data.signature = signature return deposit_data
def _get_indices_and_signatures(num_validators, message_hash, privkeys, fork, epoch): num_indices = 5 assert num_validators >= num_indices indices = random.sample(range(num_validators), num_indices) indices.sort() privkeys = [privkeys[i] for i in indices] domain_type = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( fork=fork, epoch=epoch, domain_type=domain_type, ) signatures = tuple( map(lambda key: bls.sign(message_hash, key, domain), privkeys)) return (indices, signatures)
def build_deposit_data(state, pubkey: spec.BLSPubkey, withdrawal_cred: spec.Bytes32, privkey: int, amount: int): deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + withdrawal_cred[1:], amount=amount, ) deposit_data.proof_of_possession = bls.sign( message_hash=signing_root(deposit_data), privkey=privkey, domain=spec.get_domain( state, spec.get_current_epoch(state), spec.DOMAIN_DEPOSIT, )) return deposit_data
def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( pre_state, get_current_epoch(pre_state))[-1] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH post_state = deepcopy(pre_state) voluntary_exit = VoluntaryExit( epoch=get_current_epoch(pre_state), validator_index=validator_index, ) voluntary_exit.signature = bls.sign( message_hash=signing_root(voluntary_exit), privkey=privkeys[validator_index], domain=get_domain( state=pre_state, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, )) # # Add to state via block transition # initiate_exit_block = build_empty_block_for_next_slot(post_state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) state_transition(post_state, initiate_exit_block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # # Process within epoch transition # exit_block = build_empty_block_for_next_slot(post_state) exit_block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, exit_block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return pre_state, [initiate_exit_block, exit_block], post_state
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, target_committee_size, max_deposit_amount, config): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator(proposer_pubkey, config) for _ in range(10)), validator_balances=(max_deposit_amount, ) * 10, ) block = BeaconBlock(**sample_beacon_block_params) header = block.header proposed_block = block.copy(signature=bls.sign( message_hash=header.signing_root, privkey=proposer_privkey, domain=get_domain( Fork( config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), config.GENESIS_FORK_VERSION.to_bytes(4, 'little'), config.GENESIS_EPOCH, ), slot_to_epoch(state.slot, slots_per_epoch), SignatureDomain.DOMAIN_BEACON_BLOCK, ), ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, CommitteeConfig(config), ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, CommitteeConfig(config), )
def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None): if slot is None: slot = state.slot current_epoch = get_current_epoch(state) if sender_index is None: sender_index = get_active_validator_indices(state, current_epoch)[-1] recipient_index = get_active_validator_indices(state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] if fee is None: fee = get_balance(state, sender_index) // 32 if amount is None: amount = get_balance(state, sender_index) - fee transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=fee, slot=slot, pubkey=transfer_pubkey, signature=ZERO_HASH, ) transfer.signature = bls.sign(message_hash=signing_root(transfer), privkey=transfer_privkey, domain=get_domain( state=state, domain_type=spec.DOMAIN_TRANSFER, message_epoch=get_current_epoch(state), )) # ensure withdrawal_credentials reproducable state.validator_registry[transfer.sender].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]) return transfer
def sign(path): with open(path+'/main.py','rb') as f: sha256obj = sha256() sha256obj.update(f.read()) file_hash = sha256obj.digest() # print(sha256obj) # print(file_hash) sig_path = path+'/signatures' if not os.path.exists(sig_path): os.makedirs(sig_path) for i in range(len(pubs)): sig = bls.sign(file_hash, keys[i], bytes(ecc_domain)) # print(sig) sig_file = sig_path+'/sig{}'.format(i) with open(sig_file, 'wb') as sigf: sigf.write(sig) # if __name__ == '__main__': # pass
def create_deposit_data(state, pubkey, amount): privkey = pubkey_to_privkey[pubkey] deposit_input = DepositInput( pubkey=pubkey, withdrawal_credentials=privkey.to_bytes(32, byteorder='big'), proof_of_possession=b'00' * 96, ) proof_of_possession = bls.sign(message_hash=signed_root(deposit_input), privkey=privkey, domain=get_domain( state.fork, get_current_epoch(state), spec.DOMAIN_DEPOSIT, )) deposit_input.proof_of_possession = proof_of_possession deposit_data = DepositData( amount=amount, timestamp=0, deposit_input=deposit_input, ) return deposit_data
def build_deposit_data(state, pubkey, privkey, amount): deposit_input = DepositInput( pubkey=pubkey, # insecurely use pubkey as withdrawal key as well withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:], proof_of_possession=EMPTY_SIGNATURE, ) proof_of_possession = bls.sign(message_hash=signed_root(deposit_input), privkey=privkey, domain=get_domain( state.fork, get_current_epoch(state), spec.DOMAIN_DEPOSIT, )) deposit_input.proof_of_possession = proof_of_possession deposit_data = DepositData( amount=amount, timestamp=0, deposit_input=deposit_input, ) return deposit_data