add tests for process_withdrawals
This commit is contained in:
parent
255e942f64
commit
36aae1d848
|
@ -256,11 +256,8 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||||
dequeued_withdrawals = state.withdrawals_queue[:num_withdrawals]
|
dequeued_withdrawals = state.withdrawals_queue[:num_withdrawals]
|
||||||
|
|
||||||
assert len(dequeued_withdrawals) == len(payload.withdrawals)
|
assert len(dequeued_withdrawals) == len(payload.withdrawals)
|
||||||
for dequeued_receipt, withdrawal_transaction in zip(dequeued_withdrawals, payload.withdrawals):
|
for dequeued_receipt, withdrawal in zip(dequeued_withdrawals, payload.withdrawals):
|
||||||
assert dequeued_receipt == withdrawal_transaction
|
assert dequeued_receipt == withdrawal
|
||||||
|
|
||||||
# Ensure no withdrawal type transactions in the normal payload transactions
|
|
||||||
# assert no_withdrawal_type_transactions_in(payload.transactions)
|
|
||||||
|
|
||||||
# Remove dequeued receipts from state
|
# Remove dequeued receipts from state
|
||||||
state.withdrawals_queue = state.withdrawals_queue[num_withdrawals:]
|
state.withdrawals_queue = state.withdrawals_queue[num_withdrawals:]
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
from eth2spec.test.helpers.execution_payload import (
|
||||||
|
build_empty_execution_payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later
|
||||||
|
|
||||||
|
from eth2spec.test.helpers.state import next_slot
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_withdrawals_queue(spec, state, num_withdrawals):
|
||||||
|
pre_queue_len = len(state.withdrawals_queue)
|
||||||
|
|
||||||
|
for i in range(num_withdrawals):
|
||||||
|
withdrawal = spec.Withdrawal(
|
||||||
|
index=i + 5,
|
||||||
|
address=b'\x42' * 20,
|
||||||
|
amount=200000 + i,
|
||||||
|
)
|
||||||
|
state.withdrawals_queue.append(withdrawal)
|
||||||
|
|
||||||
|
assert len(state.withdrawals_queue) == num_withdrawals + pre_queue_len
|
||||||
|
|
||||||
|
|
||||||
|
def run_withdrawals_processing(spec, state, execution_payload, valid=True):
|
||||||
|
"""
|
||||||
|
Run ``process_execution_payload``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- execution payload ('execution_payload')
|
||||||
|
- post-state ('post').
|
||||||
|
If ``valid == False``, run expecting ``AssertionError``
|
||||||
|
"""
|
||||||
|
|
||||||
|
pre_withdrawals_queue = state.withdrawals_queue.copy()
|
||||||
|
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(pre_withdrawals_queue))
|
||||||
|
|
||||||
|
yield 'pre', state
|
||||||
|
yield 'execution_payload', execution_payload
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
expect_assertion_error(lambda: spec.process_withdrawals(state, execution_payload))
|
||||||
|
yield 'post', None
|
||||||
|
return
|
||||||
|
|
||||||
|
spec.process_withdrawals(state, execution_payload)
|
||||||
|
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
|
if len(pre_withdrawals_queue) == 0:
|
||||||
|
assert len(state.withdrawals_queue) == 0
|
||||||
|
elif len(pre_withdrawals_queue) <= num_withdrawals:
|
||||||
|
assert len(state.withdrawals_queue) == 0
|
||||||
|
else:
|
||||||
|
assert state.withdrawals_queue == pre_withdrawals_queue[num_withdrawals:]
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_success_empty_queue(spec, state):
|
||||||
|
assert len(state.withdrawals_queue) == 0
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_success_one_in_queue(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_success_max_per_slot_in_queue(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_success_a_lot_in_queue(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Failure cases in which the number of withdrawals in the execution_payload is incorrect
|
||||||
|
#
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_empty_queue_non_empty_withdrawals(spec, state):
|
||||||
|
assert len(state.withdrawals_queue) == 0
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
withdrawal = spec.Withdrawal(
|
||||||
|
index=0,
|
||||||
|
address=b'\x30' * 20,
|
||||||
|
amount=420,
|
||||||
|
)
|
||||||
|
execution_payload.withdrawals.append(withdrawal)
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_one_in_queue_none_in_withdrawals(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals = []
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_one_in_queue_two_in_withdrawals(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals.append(execution_payload.withdrawals[0].copy())
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals = execution_payload.withdrawals[:-1]
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals = execution_payload.withdrawals[:-1]
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Failure cases in which the withdrawals in the execution_payload are incorrect
|
||||||
|
#
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_incorrect_dequeue_index(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals[0].index += 1
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_incorrect_dequeue_address(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals[0].address = b'\xff' * 20
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_incorrect_dequeue_amount(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, 1)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
execution_payload.withdrawals[0].amount += 1
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_one_of_many_dequeued_incorrectly(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
num_withdrawals = len(execution_payload.withdrawals)
|
||||||
|
|
||||||
|
# Pick withdrawal in middle of list and mutate
|
||||||
|
withdrawal = execution_payload.withdrawals[num_withdrawals // 2]
|
||||||
|
withdrawal.index += 1
|
||||||
|
withdrawal.address = b'\x99' * 20
|
||||||
|
withdrawal.amount += 4000000
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
||||||
|
|
||||||
|
|
||||||
|
@with_capella_and_later
|
||||||
|
@spec_state_test
|
||||||
|
def test_fail_many_dequeued_incorrectly(spec, state):
|
||||||
|
prepare_withdrawals_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
|
||||||
|
|
||||||
|
next_slot(spec, state)
|
||||||
|
execution_payload = build_empty_execution_payload(spec, state)
|
||||||
|
for i, withdrawal in enumerate(execution_payload.withdrawals):
|
||||||
|
if i % 3 == 0:
|
||||||
|
withdrawal.index += 1
|
||||||
|
elif i % 3 == 1:
|
||||||
|
withdrawal.address = (i).to_bytes(20, 'big')
|
||||||
|
else:
|
||||||
|
withdrawal.amount += 1
|
||||||
|
|
||||||
|
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
|
|
@ -28,6 +28,10 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
|
||||||
block_hash=spec.Hash32(),
|
block_hash=spec.Hash32(),
|
||||||
transactions=empty_txs,
|
transactions=empty_txs,
|
||||||
)
|
)
|
||||||
|
if spec.fork not in FORKS_BEFORE_CAPELLA:
|
||||||
|
num_withdrawals = min(spec.MAX_WITHDRAWALS_PER_PAYLOAD, len(state.withdrawals_queue))
|
||||||
|
payload.withdrawals = state.withdrawals_queue[:num_withdrawals]
|
||||||
|
|
||||||
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
|
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
|
||||||
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
|
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue