set fee recipient in empty payload fallback (#4291)
When the EL/Builder fails to produce an execution payload, we fall back to an empty `ExecutionPayload`. Even though it contains no transactions it should refer to the configured fee recipient. This is useful for privacy reasons (do not reveal the reason for the empty payload) and for compliance with additional fee recipient rules by staking pools.
This commit is contained in:
parent
02b48fafad
commit
7ad610f6d7
|
@ -440,10 +440,11 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
|
|||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
## Spec helpers
|
||||
```diff
|
||||
+ build_empty_execution_payload OK
|
||||
+ build_proof - BeaconState OK
|
||||
+ integer_squareroot OK
|
||||
```
|
||||
OK: 2/2 Fail: 0/2 Skip: 0/2
|
||||
OK: 3/3 Fail: 0/3 Skip: 0/3
|
||||
## Specific field types
|
||||
```diff
|
||||
+ root update OK
|
||||
|
@ -604,4 +605,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
|
|||
OK: 9/9 Fail: 0/9 Skip: 0/9
|
||||
|
||||
---TOTAL---
|
||||
OK: 333/338 Fail: 0/338 Skip: 5/338
|
||||
OK: 334/339 Fail: 0/339 Skip: 5/339
|
||||
|
|
|
@ -385,7 +385,8 @@ proc emptyPayloadToBlockHeader*(
|
|||
)
|
||||
|
||||
func build_empty_execution_payload*(
|
||||
state: bellatrix.BeaconState): bellatrix.ExecutionPayload =
|
||||
state: bellatrix.BeaconState,
|
||||
feeRecipient: Eth1Address): bellatrix.ExecutionPayload =
|
||||
## Assuming a pre-state of the same slot, build a valid ExecutionPayload
|
||||
## without any transactions.
|
||||
let
|
||||
|
@ -398,6 +399,7 @@ func build_empty_execution_payload*(
|
|||
|
||||
var payload = bellatrix.ExecutionPayload(
|
||||
parent_hash: latest.block_hash,
|
||||
fee_recipient: bellatrix.ExecutionAddress(data: distinctBase(feeRecipient)),
|
||||
state_root: latest.state_root, # no changes to the state
|
||||
receipts_root: EMPTY_ROOT_HASH,
|
||||
block_number: latest.block_number + 1,
|
||||
|
|
|
@ -348,12 +348,20 @@ proc getExecutionPayload[T](
|
|||
epoch: Epoch, validator_index: ValidatorIndex): Future[Opt[T]] {.async.} =
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/bellatrix/validator.md#executionpayload
|
||||
|
||||
let feeRecipient = block:
|
||||
let pubkey = node.dag.validatorKey(validator_index)
|
||||
if pubkey.isNone():
|
||||
error "Cannot get proposer pubkey, bug?", validator_index
|
||||
default(Eth1Address)
|
||||
else:
|
||||
node.getFeeRecipient(pubkey.get().toPubKey(), validator_index, epoch)
|
||||
|
||||
template empty_execution_payload(): auto =
|
||||
withState(proposalState[]):
|
||||
when stateFork >= BeaconStateFork.Capella:
|
||||
raiseAssert $capellaImplementationMissing
|
||||
elif stateFork >= BeaconStateFork.Bellatrix:
|
||||
build_empty_execution_payload(forkyState.data)
|
||||
build_empty_execution_payload(forkyState.data, feeRecipient)
|
||||
else:
|
||||
default(T)
|
||||
|
||||
|
@ -381,13 +389,6 @@ proc getExecutionPayload[T](
|
|||
default(Eth2Digest)
|
||||
latestSafe = beaconHead.safeExecutionPayloadHash
|
||||
latestFinalized = beaconHead.finalizedExecutionPayloadHash
|
||||
feeRecipient = block:
|
||||
let pubkey = node.dag.validatorKey(validator_index)
|
||||
if pubkey.isNone():
|
||||
error "Cannot get proposer pubkey, bug?", validator_index
|
||||
default(Eth1Address)
|
||||
else:
|
||||
node.getFeeRecipient(pubkey.get().toPubKey(), validator_index, epoch)
|
||||
lastFcU = node.consensusManager.forkchoiceUpdatedInfo
|
||||
timestamp = withState(proposalState[]):
|
||||
compute_timestamp_at_slot(forkyState.data, forkyState.data.slot)
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
import
|
||||
# Status libraries
|
||||
stew/bitops2,
|
||||
web3/ethtypes,
|
||||
# Beacon chain internals
|
||||
../beacon_chain/spec/[forks, helpers, state_transition],
|
||||
../beacon_chain/spec/datatypes/bellatrix,
|
||||
# Test utilities
|
||||
./unittest2, mocking/mock_genesis
|
||||
|
||||
|
@ -58,3 +60,19 @@ suite "Spec helpers":
|
|||
process(fieldVar, i shl childDepth)
|
||||
i += 1
|
||||
process(state, state.numLeaves)
|
||||
|
||||
test "build_empty_execution_payload":
|
||||
var cfg = defaultRuntimeConfig
|
||||
cfg.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
|
||||
cfg.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH
|
||||
|
||||
let state = newClone(initGenesisState(cfg = cfg).bellatrixData)
|
||||
|
||||
template testCase(recipient: Eth1Address): untyped =
|
||||
block:
|
||||
let payload = build_empty_execution_payload(state[].data, recipient)
|
||||
check payload.fee_recipient ==
|
||||
bellatrix.ExecutionAddress(data: distinctBase(recipient))
|
||||
|
||||
testCase default(Eth1Address)
|
||||
testCase Eth1Address.fromHex("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
|
||||
|
|
|
@ -145,7 +145,8 @@ proc addTestBlock*(
|
|||
if forkyState.data.slot >
|
||||
cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH + 10:
|
||||
if is_merge_transition_complete(forkyState.data):
|
||||
build_empty_execution_payload(forkyState.data)
|
||||
const feeRecipient = default(Eth1Address)
|
||||
build_empty_execution_payload(forkyState.data, feeRecipient)
|
||||
else:
|
||||
build_empty_merge_execution_payload(forkyState.data)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue