fix bug and add transfer tests
This commit is contained in:
parent
d118eb03c9
commit
c783cdb2f4
|
@ -2280,7 +2280,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
||||||
assert (
|
assert (
|
||||||
get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
|
get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
|
||||||
state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or
|
state.validator_registry[transfer.sender].activation_epoch == FAR_FUTURE_EPOCH or
|
||||||
transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(transfer.sender)
|
transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= get_balance(state, transfer.sender)
|
||||||
)
|
)
|
||||||
# Verify that the pubkey is valid
|
# Verify that the pubkey is valid
|
||||||
assert (
|
assert (
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
from copy import deepcopy
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import eth2spec.phase0.spec as spec
|
||||||
|
|
||||||
|
from eth2spec.phase0.spec import (
|
||||||
|
get_active_validator_indices,
|
||||||
|
get_balance,
|
||||||
|
get_beacon_proposer_index,
|
||||||
|
get_current_epoch,
|
||||||
|
process_transfer,
|
||||||
|
set_balance,
|
||||||
|
)
|
||||||
|
from tests.helpers import (
|
||||||
|
get_valid_transfer,
|
||||||
|
next_epoch,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# mark entire file as 'transfers'
|
||||||
|
pytestmark = pytest.mark.transfers
|
||||||
|
|
||||||
|
|
||||||
|
def run_transfer_processing(state, transfer, valid=True):
|
||||||
|
"""
|
||||||
|
Run ``process_transfer`` returning the pre and post state.
|
||||||
|
If ``valid == False``, run expecting ``AssertionError``
|
||||||
|
"""
|
||||||
|
post_state = deepcopy(state)
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
process_transfer(post_state, transfer)
|
||||||
|
return state, None
|
||||||
|
|
||||||
|
|
||||||
|
process_transfer(post_state, transfer)
|
||||||
|
|
||||||
|
proposer_index = get_beacon_proposer_index(state)
|
||||||
|
pre_transfer_sender_balance = state.balances[transfer.sender]
|
||||||
|
pre_transfer_recipient_balance = state.balances[transfer.recipient]
|
||||||
|
pre_transfer_proposer_balance = state.balances[proposer_index]
|
||||||
|
sender_balance = post_state.balances[transfer.sender]
|
||||||
|
recipient_balance = post_state.balances[transfer.recipient]
|
||||||
|
assert sender_balance == pre_transfer_sender_balance - transfer.amount - transfer.fee
|
||||||
|
assert recipient_balance == pre_transfer_recipient_balance + transfer.amount
|
||||||
|
assert post_state.balances[proposer_index] == pre_transfer_proposer_balance + transfer.fee
|
||||||
|
|
||||||
|
return state, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_success_non_activated(state):
|
||||||
|
transfer = get_valid_transfer(state)
|
||||||
|
# un-activate so validator can transfer
|
||||||
|
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_success_withdrawable(state):
|
||||||
|
next_epoch(state)
|
||||||
|
|
||||||
|
transfer = get_valid_transfer(state)
|
||||||
|
|
||||||
|
# withdrawable_epoch in past so can transfer
|
||||||
|
state.validator_registry[transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_success_active_above_max_effective(state):
|
||||||
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
|
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
||||||
|
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE + amount)
|
||||||
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_active_but_transfer_past_effective_balance(state):
|
||||||
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
|
amount = spec.MAX_EFFECTIVE_BALANCE // 32
|
||||||
|
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||||
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_incorrect_slot(state):
|
||||||
|
transfer = get_valid_transfer(state, slot=state.slot+1)
|
||||||
|
# un-activate so validator can transfer
|
||||||
|
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_insufficient_balance(state):
|
||||||
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
|
amount = spec.MAX_EFFECTIVE_BALANCE
|
||||||
|
set_balance(state, sender_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||||
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0)
|
||||||
|
|
||||||
|
# un-activate so validator can transfer
|
||||||
|
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_dust(state):
|
||||||
|
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
|
||||||
|
balance = state.balances[sender_index]
|
||||||
|
transfer = get_valid_transfer(state, sender_index=sender_index, amount=balance - spec.MIN_DEPOSIT_AMOUNT + 1, fee=0)
|
||||||
|
|
||||||
|
# un-activate so validator can transfer
|
||||||
|
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_pubkey(state):
|
||||||
|
transfer = get_valid_transfer(state)
|
||||||
|
state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH
|
||||||
|
|
||||||
|
# un-activate so validator can transfer
|
||||||
|
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
pre_state, post_state = run_transfer_processing(state, transfer, False)
|
||||||
|
|
||||||
|
return pre_state, transfer, post_state
|
|
@ -21,11 +21,13 @@ from eth2spec.phase0.spec import (
|
||||||
DepositData,
|
DepositData,
|
||||||
Eth1Data,
|
Eth1Data,
|
||||||
ProposerSlashing,
|
ProposerSlashing,
|
||||||
|
Transfer,
|
||||||
VoluntaryExit,
|
VoluntaryExit,
|
||||||
# functions
|
# functions
|
||||||
convert_to_indexed,
|
convert_to_indexed,
|
||||||
get_active_validator_indices,
|
get_active_validator_indices,
|
||||||
get_attestation_participants,
|
get_attestation_participants,
|
||||||
|
get_balance,
|
||||||
get_block_root,
|
get_block_root,
|
||||||
get_crosslink_committee_for_attestation,
|
get_crosslink_committee_for_attestation,
|
||||||
get_current_epoch,
|
get_current_epoch,
|
||||||
|
@ -291,6 +293,48 @@ def get_valid_attestation(state, slot=None):
|
||||||
return attestation
|
return attestation
|
||||||
|
|
||||||
|
|
||||||
|
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=EMPTY_SIGNATURE,
|
||||||
|
)
|
||||||
|
transfer.signature = bls.sign(
|
||||||
|
message_hash=signing_root(transfer),
|
||||||
|
privkey=transfer_privkey,
|
||||||
|
domain=get_domain(
|
||||||
|
fork=state.fork,
|
||||||
|
epoch=get_current_epoch(state),
|
||||||
|
domain_type=spec.DOMAIN_TRANSFER,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure withdrawal_credentials reproducable
|
||||||
|
state.validator_registry[transfer.sender].withdrawal_credentials = (
|
||||||
|
spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]
|
||||||
|
)
|
||||||
|
|
||||||
|
return transfer
|
||||||
|
|
||||||
|
|
||||||
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
|
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
|
||||||
message_hash = AttestationDataAndCustodyBit(
|
message_hash = AttestationDataAndCustodyBit(
|
||||||
data=attestation_data,
|
data=attestation_data,
|
||||||
|
@ -311,3 +355,9 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
|
||||||
def next_slot(state):
|
def next_slot(state):
|
||||||
block = build_empty_block_for_next_slot(state)
|
block = build_empty_block_for_next_slot(state)
|
||||||
state_transition(state, block)
|
state_transition(state, block)
|
||||||
|
|
||||||
|
|
||||||
|
def next_epoch(state):
|
||||||
|
block = build_empty_block_for_next_slot(state)
|
||||||
|
block.slot += spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH)
|
||||||
|
state_transition(state, block)
|
||||||
|
|
Loading…
Reference in New Issue