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:
parent
97bfca4b88
commit
41a3b62fe2
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue