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(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 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 validate_indexed_attestation_aggregate_signature( state: BeaconState, indexed_attestation: IndexedAttestation, slots_per_epoch: int) -> None: public_keys = tuple(state.validators[i].pubkey for i in indexed_attestation.attesting_indices) domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_ATTESTER, slots_per_epoch, indexed_attestation.data.target.epoch, ) signing_root = compute_signing_root(indexed_attestation.data, domain) bls.validate(signing_root, indexed_attestation.signature, *public_keys)
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 validate_aggregator_proof( state: BeaconState, aggregate_and_proof: AggregateAndProof, config: Eth2Config ) -> None: slot = aggregate_and_proof.aggregate.data.slot pubkey = state.validators[aggregate_and_proof.aggregator_index].pubkey 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) bls.validate(signing_root, aggregate_and_proof.selection_proof, pubkey)
def process_deposit(state: BeaconState, deposit: Deposit, config: Eth2Config) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_deposit_proof(state, deposit, DEPOSIT_CONTRACT_TREE_DEPTH) # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state = state.set("eth1_deposit_index", state.eth1_deposit_index + 1) pubkey = deposit.data.pubkey amount = deposit.data.amount withdrawal_credentials = deposit.data.withdrawal_credentials validator_pubkeys = tuple(v.pubkey for v in state.validators) if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) for new validators. # Note: The deposit contract does not check signatures. # Note: Deposits are valid across forks, thus the deposit domain # is retrieved directly from `compute_domain`. deposit_message = DepositMessage.create( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) domain = compute_domain(SignatureDomain.DOMAIN_DEPOSIT, fork_version=config.GENESIS_FORK_VERSION) signing_root = compute_signing_root(deposit_message, domain) is_valid_proof_of_possession = bls.verify(signing_root, deposit.data.signature, pubkey) if not is_valid_proof_of_possession: return state validator = Validator.create_pending_validator(pubkey, withdrawal_credentials, amount, config) return state.mset( "validators", state.validators.append(validator), "balances", state.balances.append(amount), ) else: index = ValidatorIndex(validator_pubkeys.index(pubkey)) return increase_balance(state, index, amount)
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, 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 validate_proposer_signature( state: BeaconState, signed_block: BaseSignedBeaconBlock, config: Eth2Config ) -> None: # Get the public key of proposer beacon_proposer_index = get_beacon_proposer_index(state, config) proposer_pubkey = state.validators[beacon_proposer_index].pubkey domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, config.SLOTS_PER_EPOCH ) signing_root = compute_signing_root(signed_block.message, domain) try: bls.validate(signing_root, signed_block.signature, proposer_pubkey) except SignatureError as error: raise ValidationError( f"Invalid Proposer Signature on block, beacon_proposer_index={beacon_proposer_index}", error, )
def validate_block_header_signature( state: BeaconState, header: SignedBeaconBlockHeader, pubkey: BLSPubkey, slots_per_epoch: int, ) -> None: domain = get_domain( state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch, compute_epoch_at_slot(header.message.slot, slots_per_epoch), ) signing_root = compute_signing_root(header.message, domain) try: bls.validate(signing_root, header.signature, pubkey) except SignatureError as error: raise ValidationError("Header signature is invalid:", error)
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 validate_randao_reveal( state: BeaconState, proposer_index: int, epoch: Epoch, randao_reveal: BLSSignature, slots_per_epoch: int, ) -> None: proposer = state.validators[proposer_index] proposer_pubkey = proposer.pubkey domain = get_domain(state, SignatureDomain.DOMAIN_RANDAO, slots_per_epoch) signing_root = compute_signing_root(SerializableUint64(epoch), domain) try: bls.validate(signing_root, randao_reveal, proposer_pubkey) except SignatureError as error: raise ValidationError( f"RANDAO reveal is invalid for proposer index {proposer_index} at slot {state.slot}", error, )
def _validate_voluntary_exit_signature( state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit, validator: Validator, slots_per_epoch: int, ) -> None: voluntary_exit = signed_voluntary_exit.message domain = get_domain( state, SignatureDomain.DOMAIN_VOLUNTARY_EXIT, slots_per_epoch, voluntary_exit.epoch, ) signing_root = compute_signing_root(voluntary_exit, domain) try: bls.validate(signing_root, signed_voluntary_exit.signature, validator.pubkey) except SignatureError as error: raise ValidationError( f"Invalid VoluntaryExit signature, validator_index={voluntary_exit.validator_index}", error, )
def test_validate_proposer_signature( slots_per_epoch, max_committees_per_slot, proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, target_committee_size, max_effective_balance, config, ): state = BeaconState.create(**sample_beacon_state_params).mset( "validators", tuple( create_mock_validator(proposer_pubkey, config) for _ in range(10)), "balances", (max_effective_balance, ) * 10, ) block = BeaconBlock.create(**sample_beacon_block_params) domain = get_domain(state, SignatureDomain.DOMAIN_BEACON_PROPOSER, slots_per_epoch) signing_root = compute_signing_root(block, domain) proposed_block = SignedBeaconBlock.create(message=block, signature=bls.sign( proposer_privkey, signing_root)) if is_valid_signature: validate_proposer_signature(state, proposed_block, config) else: with pytest.raises(ValidationError): validate_proposer_signature(state, proposed_block, config)
def _randao_provider_of_epoch_signature(public_key: BLSPubkey, epoch: Epoch) -> BLSSignature: privkey = private_key_provider(public_key) domain = compute_domain(SignatureDomain.DOMAIN_RANDAO) signing_root = compute_signing_root(SerializableUint64(epoch), domain) return bls.sign(privkey, signing_root)
def sign_proof_of_possession(deposit_message: DepositMessage, privkey: int) -> BLSSignature: domain = compute_domain(SignatureDomain.DOMAIN_DEPOSIT) signing_root = compute_signing_root(deposit_message, domain) return bls.sign(privkey, signing_root)