def test_invalid_private_key(backend, privkey, domain): bls.use(backend) msg = str(privkey).encode("utf-8") with pytest.raises(ValueError): bls.privtopub(privkey) with pytest.raises(ValueError): bls.sign(msg, privkey, domain=domain)
def test_validate_multiple(backend): bls.use(backend) msg = b"\x32" * 32 privkey_0 = 42 privkey_1 = 4242 pubkey_0 = bls.sk_to_pk(privkey_0) pubkey_1 = bls.sk_to_pk(privkey_1) aggregate_sig = bls.aggregate(bls.sign(privkey_0, msg), bls.sign(privkey_1, msg)) bls.validate(msg, aggregate_sig, pubkey_0, pubkey_1)
def test_validate_multiple_one_invalid(backend): bls.use(backend) msg = b"\x32" * 32 privkey_0 = 42 privkey_1 = 4242 privkey_other = 424242 pubkey_0 = bls.sk_to_pk(privkey_0) pubkey_1 = bls.sk_to_pk(privkey_1) aggregate_sig = bls.aggregate( bls.sign(privkey_0, msg), bls.sign(privkey_other, msg) ) with pytest.raises(SignatureError): bls.validate(msg, aggregate_sig, pubkey_0, pubkey_1)
def run_with( _cls, inputs: SignatureData, _config: Optional[Eth2Config] ) -> BLSSignature: # BLS override bls.use(MilagroBackend) return bls.sign(inputs["privkey"], inputs["message"])
def test_randao_reveal_validation( is_valid, epoch, expected_epoch, proposer_key_index, expected_proposer_key_index, privkeys, pubkeys, sample_fork_params, genesis_state, config, ): state = genesis_state.set( "slot", compute_start_slot_at_epoch(epoch, config.SLOTS_PER_EPOCH)) slots_per_epoch = config.SLOTS_PER_EPOCH domain = get_domain(state, SignatureDomain.DOMAIN_RANDAO, slots_per_epoch) signing_root = compute_signing_root(SerializableUint64(epoch), domain) proposer_privkey = privkeys[proposer_key_index] randao_reveal = bls.sign(proposer_privkey, signing_root) try: validate_randao_reveal( state=state, proposer_index=expected_proposer_key_index, epoch=expected_epoch, randao_reveal=randao_reveal, slots_per_epoch=slots_per_epoch, ) except ValidationError: if is_valid: raise else: if not is_valid: pytest.fail("Did not raise")
def sign_proof_of_possession(deposit_data: DepositData, privkey: int) -> BLSSignature: return bls.sign( message_hash=deposit_data.signing_root, privkey=privkey, domain=bls_domain(SignatureDomain.DOMAIN_DEPOSIT), )
def test_validate(backend): bls.use(backend) msg = b"\x32" * 32 privkey = 42 pubkey = bls.sk_to_pk(privkey) sig = bls.sign(privkey, msg) bls.validate(msg, sig, pubkey)
def test_bls_core_succeed(backend, privkey): bls.use(backend) domain = 0 msg = str(privkey).encode('utf-8') sig = bls.sign(msg, privkey, domain=domain) pub = bls.privtopub(privkey) assert bls.verify(msg, pub, sig, domain=domain)
def sign_proof_of_possession(deposit_message: DepositMessage, privkey: int) -> BLSSignature: return bls.sign( message_hash=deposit_message.hash_tree_root, privkey=privkey, domain=compute_domain(SignatureDomain.DOMAIN_DEPOSIT), )
def test_signature_aggregation(backend, msg, privkeys, domain): bls.use(backend) sigs = [bls.sign(msg, k, domain=domain) for k in privkeys] pubs = [bls.privtopub(k) for k in privkeys] aggsig = bls.aggregate_signatures(sigs) aggpub = bls.aggregate_pubkeys(pubs) assert bls.verify(msg, aggpub, aggsig, domain=domain)
def test_randao_processing_validates_randao_reveal( sample_beacon_block_params, sample_beacon_block_body_params, sample_beacon_state_params, sample_fork_params, keymap, config, ): proposer_pubkey, proposer_privkey = first(keymap.items()) state = SerenityBeaconState.create(**sample_beacon_state_params).mset( "validators", tuple( create_mock_validator(proposer_pubkey, config) for _ in range(config.TARGET_COMMITTEE_SIZE)), "balances", (config.MAX_EFFECTIVE_BALANCE, ) * config.TARGET_COMMITTEE_SIZE, "randao_mixes", tuple(ZERO_HASH32 for _ in range(config.EPOCHS_PER_HISTORICAL_VECTOR)), ) epoch = state.current_epoch(config.SLOTS_PER_EPOCH) message_hash = (epoch + 1).to_bytes(32, byteorder="little") domain = get_domain(state, SignatureDomain.DOMAIN_RANDAO, config.SLOTS_PER_EPOCH) randao_reveal = bls.sign(message_hash, proposer_privkey, domain) block_body = BeaconBlockBody.create(**sample_beacon_block_body_params).set( "randao_reveal", randao_reveal) block = SerenityBeaconBlock.create(**sample_beacon_block_params).set( "body", block_body) with pytest.raises(ValidationError): process_randao(state, block, config)
def _randao_provider_of_epoch_signature(public_key: BLSPubkey, epoch: Epoch) -> BLSSignature: private_key = private_key_provider(public_key) # TODO: fix how we get the signing root message = ssz.get_hash_tree_root(epoch, sedes=ssz.sedes.uint64) domain = compute_domain(SignatureDomain.DOMAIN_RANDAO) sig = bls.sign(message, private_key, domain) return sig
def sign(duty: Duty, operation: Operation, private_key_provider: PrivateKeyProvider) -> BLSSignature: message = operation.hash_tree_root private_key = private_key_provider(duty.validator_public_key) # TODO use correct ``domain`` value # NOTE currently only uses part of the domain value # need to get fork from the state and compute the full domain value locally domain = Domain(b"\x00" * 4 + signature_domain_to_domain_type(duty.signature_domain)) return bls.sign(message, private_key, domain)
def test_sanity(backend): bls.use(backend) msg_0 = b"\x32" * 32 domain = 123 # Test: Verify the basic sign/verify process privkey_0 = 5566 sig_0 = bls.sign(msg_0, privkey_0, domain) assert_signature(sig_0) pubkey_0 = bls.privtopub(privkey_0) assert_pubkey(pubkey_0) assert bls.verify(msg_0, pubkey_0, sig_0, domain) privkey_1 = 5567 sig_1 = bls.sign(msg_0, privkey_1, domain) pubkey_1 = bls.privtopub(privkey_1) assert bls.verify(msg_0, pubkey_1, sig_1, domain) # Test: Verify signatures are correctly aggregated aggregated_signature = bls.aggregate_signatures([sig_0, sig_1]) assert_signature(aggregated_signature) # Test: Verify pubkeys are correctly aggregated aggregated_pubkey = bls.aggregate_pubkeys([pubkey_0, pubkey_1]) assert_pubkey(aggregated_pubkey) # Test: Verify with `aggregated_signature` and `aggregated_pubkey` assert bls.verify(msg_0, aggregated_pubkey, aggregated_signature, domain) # Test: `verify_multiple` msg_1 = b"x22" * 32 privkey_2 = 55688 sig_2 = bls.sign(msg_1, privkey_2, domain) assert_signature(sig_2) pubkey_2 = bls.privtopub(privkey_2) assert_pubkey(pubkey_2) sig_1_2 = bls.aggregate_signatures([sig_1, sig_2]) assert bls.verify_multiple( pubkeys=[pubkey_1, pubkey_2], message_hashes=[msg_0, msg_1], signature=sig_1_2, domain=domain, )
def sign_transaction(*, message_hash: Hash32, privkey: int, state: BeaconState, slot: Slot, signature_domain: SignatureDomain, slots_per_epoch: int) -> BLSSignature: domain = get_domain( state, signature_domain, slots_per_epoch, message_epoch=compute_epoch_at_slot(slot, slots_per_epoch), ) return bls.sign(message_hash=message_hash, privkey=privkey, domain=domain)
def sign(duty: Duty, operation: Operation, private_key_provider: PrivateKeyProvider) -> BLSSignature: privkey = private_key_provider(duty.validator_public_key) # TODO use correct ``domain`` value # NOTE currently only uses part of the domain value # need to get fork from the state and compute the full domain value locally domain = compute_domain(duty.signature_domain) signing_root = compute_signing_root(operation, domain) return bls.sign(privkey, signing_root)
def run_with(_cls, inputs: SignatureDescriptor, _config: Optional[Eth2Config]) -> BLSSignature: # BLS override bls.use(MilagroBackend) return bls.sign( cast(Hash32, inputs["message_hash"]), int(inputs["privkey"]), cast(Domain, (inputs["domain"])), )
def _randao_provider_of_epoch_signature( public_key: BLSPubkey, epoch: Epoch ) -> BLSSignature: private_key = private_key_provider(public_key) # TODO: fix how we get the signing root message = Hash32(epoch.to_bytes(32, byteorder="big")) domain = Domain( b"\x00" * 4 + signature_domain_to_domain_type(SignatureDomain.DOMAIN_RANDAO) ) sig = bls.sign(message, private_key, domain) return sig
def get_slot_signature(state: BeaconState, slot: Slot, privkey: int, config: Eth2Config) -> BLSSignature: """ Sign on ``slot`` and return the signature. """ domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, config.SLOTS_PER_EPOCH, message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH), ) return bls.sign(get_hash_tree_root(slot, sedes=uint64), privkey, domain)
def get_block_signature(state: BeaconState, block: BeaconBlock, private_key: int, slots_per_epoch: int) -> BLSSignature: epoch = compute_epoch_at_slot(block.slot, slots_per_epoch) domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, message_epoch=epoch, ) signing_root = compute_signing_root(block, domain) return bls.sign(private_key, signing_root)
def test_multi_aggregation(backend, msg_1, msg_2, privkeys_1, privkeys_2, domain): bls.use(backend) sigs_1 = [bls.sign(msg_1, k, domain=domain) for k in privkeys_1] # signatures to msg_1 pubs_1 = [bls.privtopub(k) for k in privkeys_1] aggpub_1 = bls.aggregate_pubkeys(pubs_1) # sig_1 to msg_1 sigs_2 = [bls.sign(msg_2, k, domain=domain) for k in privkeys_2] # signatures to msg_2 pubs_2 = [bls.privtopub(k) for k in privkeys_2] aggpub_2 = bls.aggregate_pubkeys(pubs_2) # sig_2 to msg_2 message_hashes = [msg_1, msg_2] pubs = [aggpub_1, aggpub_2] aggsig = bls.aggregate_signatures(sigs_1 + sigs_2) assert bls.verify_multiple(pubkeys=pubs, message_hashes=message_hashes, signature=aggsig, domain=domain)
def get_slot_signature( state: BeaconState, slot: Slot, privkey: int, config: Eth2Config ) -> BLSSignature: """ Sign on ``slot`` and return the signature. """ domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, config.SLOTS_PER_EPOCH, message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH), ) signing_root = compute_signing_root(SerializableUint64(slot), domain) return bls.sign(privkey, signing_root)
def test_aggregate_votes(votes_count, random, privkeys, pubkeys): bit_count = 10 pre_bitfield = get_empty_bitfield(bit_count) pre_sigs = () domain = compute_domain(SignatureDomain.DOMAIN_ATTESTATION) 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, attesting_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) if votes_count == 0 and bls.backend in (ChiaBackend, MilagroBackend): with pytest.raises(ValidationError): bls.validate(message_hash, aggregated_pubs, sigs, domain) else: bls.validate(message_hash, aggregated_pubs, sigs, domain)
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_effective_balance, config): state = BeaconState(**sample_beacon_state_params).copy( validators=tuple( create_mock_validator(proposer_pubkey, config) for _ in range(10) ), balances=(max_effective_balance,) * 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( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, ), ), ) 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_indices_and_signatures(validator_count, state, config, attestation, privkeys): num_indices = 5 assert validator_count >= num_indices indices = random.sample(range(validator_count), num_indices) indices.sort() privkeys = [privkeys[i] for i in indices] domain = get_domain( state=state, signature_domain=SignatureDomain.DOMAIN_BEACON_ATTESTER, slots_per_epoch=config.SLOTS_PER_EPOCH, ) signing_root = compute_signing_root(attestation.data, domain) signatures = tuple(map(lambda key: bls.sign(key, signing_root), privkeys)) return (indices, signatures)
def sign(duty: Duty, operation: Operation, private_key_provider: PrivateKeyProvider) -> BLSSignature: privkey = private_key_provider(duty.validator_public_key) # TODO use correct ``domain`` value # NOTE currently only uses part of the domain value # need to get fork from the state and compute the full domain value locally # NOTE: hardcoded for testing, based on generating the minimal set of validators genesis_validators_root = Root( Hash32( bytes.fromhex( "83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda" ))) domain = compute_domain(duty.signature_domain, genesis_validators_root=genesis_validators_root) signing_root = compute_signing_root(operation, domain) return bls.sign(privkey, signing_root)
def _get_indices_and_signatures(validator_count, state, config, message_hash, privkeys): num_indices = 5 assert validator_count >= num_indices indices = random.sample(range(validator_count), num_indices) indices.sort() privkeys = [privkeys[i] for i in indices] signature_domain = SignatureDomain.DOMAIN_ATTESTATION domain = get_domain( state=state, signature_domain=signature_domain, slots_per_epoch=config.SLOTS_PER_EPOCH, ) signatures = tuple( map(lambda key: bls.sign(message_hash, key, domain), privkeys)) return (indices, signatures)
def sign_transaction( *, object: ssz.Serializable, privkey: int, state: BeaconState, slot: Slot, signature_domain: SignatureDomain, slots_per_epoch: int, ) -> BLSSignature: domain = get_domain( state, signature_domain, slots_per_epoch, message_epoch=compute_epoch_at_slot(slot, slots_per_epoch), ) signing_root = compute_signing_root(object, domain) return bls.sign(privkey, signing_root)
def _randao_provider_of_epoch_signature( public_key: BLSPubkey, epoch: Epoch ) -> BLSSignature: privkey = private_key_provider(public_key) # NOTE: hardcoded for testing, based on generating the minimal set of validators genesis_validators_root = Root( Hash32( bytes.fromhex( "83431ec7fcf92cfc44947fc0418e831c25e1d0806590231c439830db7ad54fda" ) ) ) domain = compute_domain( SignatureDomain.DOMAIN_RANDAO, genesis_validators_root=genesis_validators_root, ) signing_root = compute_signing_root(SerializableUint64(epoch), domain) return bls.sign(privkey, signing_root)
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, attesting_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) if votes_count != 0: bls.validate(message_hash, aggregated_pubs, sigs, domain) else: # EMPTY_SIGNATURE is considered invalid with pytest.raises(ValueError): bls.validate(message_hash, aggregated_pubs, sigs, domain)