update `wss_sim` for Bellatrix, Capella and Deneb (#6170)

The `wss_sim` was not properly maintained since Bellatrix. The missing
functionality is now added, including:

- Bellatrix: Connect to an EL for execution payload production
- Capella: Correct withdrawals processing, is mandatory to do
- Deneb: Dump blob sidecars into the output directory

See https://ethresear.ch/t/insecura-my-consensus-for-the-pyrmont-network/11833
This commit is contained in:
Etan Kissling 2024-04-08 15:28:46 +02:00 committed by GitHub
parent 97bfca4b88
commit 41a3b62fe2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 184 additions and 80 deletions

View File

@ -36,6 +36,15 @@ proc dump*(dir: string, v: ForkySignedBeaconBlock) =
logErrors: logErrors:
SSZ.saveFile(dir / &"block-{v.message.slot}-{shortLog(v.root)}.ssz", v) SSZ.saveFile(dir / &"block-{v.message.slot}-{shortLog(v.root)}.ssz", v)
proc dump*(dir: string, v: BlobSidecar) =
logErrors:
let
slot = v.signed_block_header.message.slot
blck = shortLog(v.signed_block_header.message.hash_tree_root())
index = v.index
root = shortLog(v.hash_tree_root())
SSZ.saveFile(dir / &"blob-{slot}-{blck}-{index}-{root}.ssz", v)
proc dump*(dir: string, v: ForkyHashedBeaconState) = proc dump*(dir: string, v: ForkyHashedBeaconState) =
mixin saveFile mixin saveFile
logErrors: logErrors:

View File

@ -17,14 +17,19 @@ import
confutils, confutils,
stew/io2, stew/io2,
../tests/testblockutil, ../tests/testblockutil,
../beacon_chain/el/el_manager,
../beacon_chain/networking/network_metadata, ../beacon_chain/networking/network_metadata,
../beacon_chain/[beacon_clock, sszdump], ../beacon_chain/[beacon_clock, sszdump],
../beacon_chain/spec/eth2_apis/eth2_rest_serialization, ../beacon_chain/spec/eth2_apis/eth2_rest_serialization,
../beacon_chain/spec/datatypes/[phase0, altair, bellatrix], ../beacon_chain/spec/datatypes/[phase0, altair, bellatrix],
../beacon_chain/spec/[ ../beacon_chain/spec/[
beaconstate, crypto, forks, helpers, signatures, state_transition], beaconstate, crypto, engine_authentication, forks, helpers,
signatures, state_transition],
../beacon_chain/validators/[keystore_management, validator_pool] ../beacon_chain/validators/[keystore_management, validator_pool]
from ../beacon_chain/gossip_processing/block_processor import
newExecutionPayload
template findIt*(s: openArray, predicate: untyped): int = template findIt*(s: openArray, predicate: untyped): int =
var res = -1 var res = -1
for i, it {.inject.} in s: for i, it {.inject.} in s:
@ -45,15 +50,96 @@ from ../beacon_chain/spec/datatypes/capella import SignedBeaconBlock
from ../beacon_chain/spec/datatypes/deneb import SignedBeaconBlock from ../beacon_chain/spec/datatypes/deneb import SignedBeaconBlock
cli do(validatorsDir: string, secretsDir: string, cli do(validatorsDir: string, secretsDir: string,
startState: string, network: string): startState: string, startBlock: string,
network: string, elUrl: string, jwtSecret: string,
suggestedFeeRecipient: string, graffiti = "insecura"):
let let
cfg = getMetadataForNetwork(network).cfg metadata = getMetadataForNetwork(network)
state = cfg = metadata.cfg
state = block:
let data = readAllBytes(startState)
if data.isErr:
fatal "failed to read hashed beacon state", err = $data.error
quit QuitFailure
try: try:
newClone(readSszForkedHashedBeaconState( newClone(readSszForkedHashedBeaconState(cfg, data.get))
cfg, readAllBytes(startState).tryGet())) except SerializationError as exc:
except CatchableError: fatal "failed to parse hashed beacon state", err = exc.msg
raiseAssert "failed to read hashed beacon state" quit QuitFailure
blck = block:
let data = readAllBytes(startBlock)
if data.isErr:
fatal "failed to read signed beacon block", err = $data.error
quit QuitFailure
try:
newClone(readSszForkedSignedBeaconBlock(cfg, data.get))
except SerializationError as exc:
fatal "failed to parse signed beacon block", err = exc.msg
quit QuitFailure
engineApiUrl = block:
let
jwtSecretFile =
try:
InputFile.parseCmdArg(jwtSecret)
except ValueError as exc:
fatal "failed to read JWT secret file", err = exc.msg
quit QuitFailure
jwtSecret = loadJwtSecretFile(jwtSecretFile)
if jwtSecret.isErr:
fatal "failed to parse JWT secret file", err = jwtSecret.error
quit QuitFailure
let finalUrl = EngineApiUrlConfigValue(url: elUrl)
.toFinalUrl(Opt.some jwtSecret.get)
if finalUrl.isErr:
fatal "failed to read EL URL", err = finalUrl.error
quit QuitFailure
finalUrl.get
elManager = ELManager.new(
cfg,
metadata.depositContractBlock,
metadata.depositContractBlockHash,
db = nil,
@[engineApiUrl],
metadata.eth1Network)
feeRecipient =
try:
Address.fromHex(suggestedFeeRecipient)
except ValueError as exc:
fatal "failed to parse suggested fee recipient", err = exc.msg
quit QuitFailure
graffitiValue =
try:
GraffitiBytes.init(graffiti)
except ValueError as exc:
fatal "failed to parse graffiti", err = exc.msg
quit QuitFailure
# Sync EL to initial state. Note that to construct the new branch, the EL
# should not have advanced to a later block via `engine_forkchoiceUpdated`.
# The EL may otherwise refuse to produce new heads
elManager.start(syncChain = false)
withBlck(blck[]):
when consensusFork >= ConsensusFork.Bellatrix:
if forkyBlck.message.is_execution_block:
template payload(): auto = forkyBlck.message.body.execution_payload
if not payload.block_hash.isZero:
notice "Syncing EL", elUrl, jwtSecret
while true:
waitFor noCancel sleepAsync(chronos.seconds(2))
(waitFor noCancel elManager
.newExecutionPayload(forkyBlck.message)).isOkOr:
continue
let (status, _) = waitFor noCancel elManager.forkchoiceUpdated(
headBlockHash = payload.block_hash,
safeBlockHash = payload.block_hash,
finalizedBlockHash = ZERO_HASH,
payloadAttributes = none(consensusFork.PayloadAttributes))
if status != PayloadExecutionStatus.valid:
continue
notice "EL synced", elUrl, jwtSecret
break
var var
clock = BeaconClock.init(getStateField(state[], genesis_time)).valueOr: clock = BeaconClock.init(getStateField(state[], genesis_time)).valueOr:
@ -72,7 +158,7 @@ cli do(validatorsDir: string, secretsDir: string,
validators[idx.get()] = item.privateKey validators[idx.get()] = item.privateKey
validatorKeys[pubkey] = item.privateKey validatorKeys[pubkey] = item.privateKey
else: else:
warn "Unkownn validator", pubkey warn "Unknown validator", pubkey
var var
blockRoot = withState(state[]): forkyState.latest_block_root blockRoot = withState(state[]): forkyState.latest_block_root
@ -159,80 +245,89 @@ cli do(validatorsDir: string, secretsDir: string,
# should never fall back to default value # should never fall back to default value
validators.getOrDefault( validators.getOrDefault(
proposer, default(ValidatorPrivKey))).toValidatorSig() proposer, default(ValidatorPrivKey))).toValidatorSig()
message = makeBeaconBlock( withState(state[]):
cfg, let
state[], payload =
proposer, when consensusFork >= ConsensusFork.Bellatrix:
randao_reveal, let
getStateField(state[], eth1_data), executionHead =
static(GraffitiBytes.init("insecura")), forkyState.data.latest_execution_payload_header.block_hash
blockAggregates, withdrawals =
@[], when consensusFork >= ConsensusFork.Capella:
BeaconBlockValidatorChanges(), get_expected_withdrawals(forkyState.data)
syncAggregate, else:
default(bellatrix.ExecutionPayloadForSigning), newSeq[Withdrawal]()
noRollback,
cache).get()
try: var pl: consensusFork.ExecutionPayloadForSigning
case message.kind while true:
of ConsensusFork.Phase0: pl = (waitFor noCancel elManager.getPayload(
blockRoot = hash_tree_root(message.phase0Data) consensusFork.ExecutionPayloadForSigning,
let signedBlock = phase0.SignedBeaconBlock( consensusHead = forkyState.latest_block_root,
message: message.phase0Data, headBlock = executionHead,
safeBlock = executionHead,
finalizedBlock = ZERO_HASH,
timestamp = compute_timestamp_at_slot(
forkyState.data, forkyState.data.slot),
randomData = get_randao_mix(
forkyState.data, get_current_epoch(forkyState.data)),
suggestedFeeRecipient = feeRecipient,
withdrawals = withdrawals)).valueOr:
waitFor noCancel sleepAsync(chronos.seconds(2))
continue
break
pl
else:
default(bellatrix.ExecutionPayloadForSigning)
message = makeBeaconBlock(
cfg,
state[],
proposer,
randao_reveal,
forkyState.data.eth1_data,
graffitiValue,
blockAggregates,
@[],
BeaconBlockValidatorChanges(),
syncAggregate,
payload,
noRollback,
cache).get()
blockRoot = message.forky(consensusFork).hash_tree_root()
let
proposerPrivkey =
try:
validators[proposer]
except KeyError as exc:
raiseAssert "Proposer key not available: " & exc.msg
signedBlock = consensusFork.SignedBeaconBlock(
message: message.forky(consensusFork),
root: blockRoot, root: blockRoot,
signature: get_block_signature( signature: get_block_signature(
fork, genesis_validators_root, slot, blockRoot, fork, genesis_validators_root, slot, blockRoot,
validators[proposer]).toValidatorSig()) proposerPrivkey).toValidatorSig())
dump(".", signedBlock)
of ConsensusFork.Altair: dump(".", signedBlock)
blockRoot = hash_tree_root(message.altairData) when consensusFork >= ConsensusFork.Deneb:
let signedBlock = altair.SignedBeaconBlock( let blobs = signedBlock.create_blob_sidecars(
message: message.altairData, payload.blobsBundle.proofs, payload.blobsBundle.blobs)
root: blockRoot, for blob in blobs:
signature: get_block_signature( dump(".", blob)
fork, genesis_validators_root, slot, blockRoot,
validators[proposer]).toValidatorSig()) notice "Block proposed", message, blockRoot
dump(".", signedBlock)
of ConsensusFork.Bellatrix: when consensusFork >= ConsensusFork.Bellatrix:
blockRoot = hash_tree_root(message.bellatrixData) while true:
let signedBlock = bellatrix.SignedBeaconBlock( let status = waitFor noCancel elManager
message: message.bellatrixData, .newExecutionPayload(signedBlock.message)
root: blockRoot, if status.isNone:
signature: get_block_signature( waitFor noCancel sleepAsync(chronos.seconds(2))
fork, genesis_validators_root, slot, blockRoot, continue
validators[proposer]).toValidatorSig()) doAssert status.get in [
dump(".", signedBlock) PayloadExecutionStatus.valid,
of ConsensusFork.Capella: PayloadExecutionStatus.accepted,
blockRoot = hash_tree_root(message.capellaData) PayloadExecutionStatus.syncing]
let signedBlock = capella.SignedBeaconBlock( break
message: message.capellaData,
root: blockRoot,
signature: get_block_signature(
fork, genesis_validators_root, slot, blockRoot,
validators[proposer]).toValidatorSig())
dump(".", signedBlock)
of ConsensusFork.Deneb:
blockRoot = hash_tree_root(message.denebData)
let signedBlock = deneb.SignedBeaconBlock(
message: message.denebData,
root: blockRoot,
signature: get_block_signature(
fork, genesis_validators_root, slot, blockRoot,
validators[proposer]).toValidatorSig())
dump(".", signedBlock)
of ConsensusFork.Electra:
blockRoot = hash_tree_root(message.electraData)
let signedBlock = electra.SignedBeaconBlock(
message: message.electraData,
root: blockRoot,
signature: get_block_signature(
fork, genesis_validators_root, slot, blockRoot,
validators[proposer]).toValidatorSig())
dump(".", signedBlock)
except CatchableError:
raiseAssert "unreachable"
notice "Block proposed", message, blockRoot
aggregates.setLen(0) aggregates.setLen(0)