From 34853ca15527752c48b2dba3d56da56f01364aa7 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Wed, 22 May 2024 01:03:38 +0300 Subject: [PATCH 1/8] VC: Deprecate fallback path to produceBlockV2, use produceBlockV3 always. (#6300) * Remove fallback path to publishBlockV2(), use publishBlockV3(). * Update AllTests.md. --- AllTests-mainnet.md | 2 +- beacon_chain/validator_client/api.nim | 109 ----- .../validator_client/block_service.nim | 387 +----------------- beacon_chain/validator_client/common.nim | 1 - 4 files changed, 13 insertions(+), 486 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 534e465dd..ebdb530e5 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -1030,4 +1030,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 686/691 Fail: 0/691 Skip: 5/691 +OK: 687/692 Fail: 0/692 Skip: 5/692 diff --git a/beacon_chain/validator_client/api.nim b/beacon_chain/validator_client/api.nim index 75d171ddc..5762bf088 100644 --- a/beacon_chain/validator_client/api.nim +++ b/beacon_chain/validator_client/api.nim @@ -2022,97 +2022,6 @@ proc publishContributionAndProofs*( raise (ref ValidatorApiError)( msg: "Failed to publish sync committee contribution", data: failures) -proc produceBlockV2*( - vc: ValidatorClientRef, - slot: Slot, - randao_reveal: ValidatorSig, - graffiti: GraffitiBytes, - strategy: ApiStrategyKind - ): Future[ProduceBlockResponseV2] {.async.} = - const - RequestName = "produceBlockV2" - - var failures: seq[ApiNodeFailure] - - case strategy - of ApiStrategyKind.First, ApiStrategyKind.Best: - let res = vc.firstSuccessParallel( - RestPlainResponse, - ProduceBlockResponseV2, - SlotDuration, - ViableNodeStatus, - {BeaconNodeRole.BlockProposalData}, - produceBlockV2Plain(it, slot, randao_reveal, graffiti)): - if apiResponse.isErr(): - handleCommunicationError() - ApiResponse[ProduceBlockResponseV2].err(apiResponse.error) - else: - let response = apiResponse.get() - case response.status: - of 200: - let - version = response.headers.getString("eth-consensus-version") - res = decodeBytes(ProduceBlockResponseV2, response.data, - response.contentType, version) - if res.isErr(): - handleUnexpectedData() - ApiResponse[ProduceBlockResponseV2].err($res.error) - else: - ApiResponse[ProduceBlockResponseV2].ok(res.get()) - of 400: - handle400() - ApiResponse[ProduceBlockResponseV2].err(ResponseInvalidError) - of 500: - handle500() - ApiResponse[ProduceBlockResponseV2].err(ResponseInternalError) - of 503: - handle503() - ApiResponse[ProduceBlockResponseV2].err(ResponseNoSyncError) - else: - handleUnexpectedCode() - ApiResponse[ProduceBlockResponseV2].err(ResponseUnexpectedError) - - if res.isErr(): - raise (ref ValidatorApiError)(msg: res.error, data: failures) - return res.get() - - of ApiStrategyKind.Priority: - vc.firstSuccessSequential( - RestPlainResponse, - SlotDuration, - ViableNodeStatus, - {BeaconNodeRole.BlockProposalData}, - produceBlockV2Plain(it, slot, randao_reveal, graffiti)): - if apiResponse.isErr(): - handleCommunicationError() - false - else: - let response = apiResponse.get() - case response.status: - of 200: - let - version = response.headers.getString("eth-consensus-version") - res = decodeBytes(ProduceBlockResponseV2, response.data, - response.contentType, version) - if res.isOk(): return res.get() - handleUnexpectedData() - false - of 400: - handle400() - false - of 500: - handle500() - false - of 503: - handle503() - false - else: - handleUnexpectedCode() - false - - raise (ref ValidatorApiError)( - msg: "Failed to produce block", data: failures) - proc produceBlockV3*( vc: ValidatorClientRef, slot: Slot, @@ -2161,24 +2070,15 @@ proc produceBlockV3*( ApiResponse[ProduceBlockResponseV3].ok(res.get()) of 400: handle400() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) - ApiResponse[ProduceBlockResponseV3].err(ResponseInvalidError) - of 404: - # TODO (cheatfate): Remove this handler when produceBlockV2 support - # will be dropped. - handle400() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) ApiResponse[ProduceBlockResponseV3].err(ResponseInvalidError) of 500: handle500() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) ApiResponse[ProduceBlockResponseV3].err(ResponseInternalError) of 503: handle503() ApiResponse[ProduceBlockResponseV3].err(ResponseNoSyncError) else: handleUnexpectedCode() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) ApiResponse[ProduceBlockResponseV3].err(ResponseUnexpectedError) if res.isErr(): @@ -2216,24 +2116,15 @@ proc produceBlockV3*( false of 400: handle400() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) - false - of 404: - # TODO (cheatfate): Remove this handler when produceBlockV2 support - # will be dropped. - handle400() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) false of 500: handle500() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) false of 503: handle503() false else: handleUnexpectedCode() - node.features.incl(RestBeaconNodeFeature.NoProduceBlockV3) false raise (ref ValidatorApiError)( diff --git a/beacon_chain/validator_client/block_service.nim b/beacon_chain/validator_client/block_service.nim index 30198ac9e..1bd6514b7 100644 --- a/beacon_chain/validator_client/block_service.nim +++ b/beacon_chain/validator_client/block_service.nim @@ -49,114 +49,6 @@ func shortLog(v: ForkedMaybeBlindedBeaconBlock): auto = proc proposeBlock(vc: ValidatorClientRef, slot: Slot, proposerKey: ValidatorPubKey) {.async.} -proc produceBlock( - vc: ValidatorClientRef, - currentSlot, slot: Slot, - randao_reveal: ValidatorSig, - graffiti: GraffitiBytes, - validator: AttachedValidator - ): Future[Opt[PreparedBeaconBlock]] {.async.} = - logScope: - slot = slot - wall_slot = currentSlot - validator = validatorLog(validator) - let - produceBlockResponse = - try: - await vc.produceBlockV2(slot, randao_reveal, graffiti, - ApiStrategyKind.Best) - except ValidatorApiError as exc: - warn "Unable to retrieve block data", reason = exc.getFailureReason() - return Opt.none(PreparedBeaconBlock) - except CancelledError as exc: - debug "Block data production has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while getting block data", - error_name = exc.name, error_msg = exc.msg - return Opt.none(PreparedBeaconBlock) - case produceBlockResponse.kind - of ConsensusFork.Phase0: - let blck = produceBlockResponse.phase0Data - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.none(deneb.KzgProofs), - blobsOpt: Opt.none(deneb.Blobs))) - of ConsensusFork.Altair: - let blck = produceBlockResponse.altairData - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.none(deneb.KzgProofs), - blobsOpt: Opt.none(deneb.Blobs))) - of ConsensusFork.Bellatrix: - let blck = produceBlockResponse.bellatrixData - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.none(deneb.KzgProofs), - blobsOpt: Opt.none(deneb.Blobs))) - of ConsensusFork.Capella: - let blck = produceBlockResponse.capellaData - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.none(deneb.KzgProofs), - blobsOpt: Opt.none(deneb.Blobs))) - of ConsensusFork.Deneb: - let - blck = produceBlockResponse.denebData.`block` - kzgProofs = produceBlockResponse.denebData.kzg_proofs - blobs = produceBlockResponse.denebData.blobs - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.some(kzgProofs), - blobsOpt: Opt.some(blobs))) - of ConsensusFork.Electra: - let - blck = produceBlockResponse.electraData.`block` - kzgProofs = produceBlockResponse.electraData.kzg_proofs - blobs = produceBlockResponse.electraData.blobs - return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck), - data: ForkedBeaconBlock.init(blck), - kzgProofsOpt: Opt.some(kzgProofs), - blobsOpt: Opt.some(blobs))) - -proc produceBlindedBlock( - vc: ValidatorClientRef, - currentSlot, slot: Slot, - randao_reveal: ValidatorSig, - graffiti: GraffitiBytes, - validator: AttachedValidator - ): Future[Opt[PreparedBlindedBeaconBlock]] {.async.} = - logScope: - slot = slot - wall_slot = currentSlot - validator = validatorLog(validator) - let - beaconBlock = - try: - await vc.produceBlindedBlock(slot, randao_reveal, graffiti, - ApiStrategyKind.Best) - except ValidatorApiError as exc: - warn "Unable to retrieve blinded block data", error_msg = exc.msg, - reason = exc.getFailureReason() - return Opt.none(PreparedBlindedBeaconBlock) - except CancelledError as exc: - debug "Blinded block data production has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while getting blinded block data", - error_name = exc.name, error_msg = exc.msg - return Opt.none(PreparedBlindedBeaconBlock) - blockRoot = withBlck(beaconBlock): hash_tree_root(forkyBlck) - - return Opt.some( - PreparedBlindedBeaconBlock(blockRoot: blockRoot, data: beaconBlock)) - -proc lazyWait[T](fut: Future[T]) {.async.} = - try: - discard await fut - except CatchableError: - discard - proc prepareRandao(vc: ValidatorClientRef, slot: Slot, proposerKey: ValidatorPubKey) {.async.} = if slot == GENESIS_SLOT: @@ -209,21 +101,9 @@ proc spawnProposalTask(vc: ValidatorClientRef, duty: duty ) -proc isProduceBlockV3Supported(vc: ValidatorClientRef): bool = - let - # Both `statuses` and `roles` should be set to values which are used in - # api.produceBlockV3() call. - statuses = ViableNodeStatus - roles = {BeaconNodeRole.BlockProposalData} - - for node in vc.filterNodes(statuses, roles): - if RestBeaconNodeFeature.NoProduceBlockV3 notin node.features: - return true - false - proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, fork: Fork, randaoReveal: ValidatorSig, - validator: AttachedValidator): Future[bool] {.async.} = + validator: AttachedValidator) {.async.} = let genesisRoot = vc.beaconGenesis.genesis_validators_root graffiti = @@ -247,14 +127,14 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, ApiStrategyKind.Best) except ValidatorApiError as exc: warn "Unable to retrieve block data", reason = exc.getFailureReason() - return false + return except CancelledError as exc: debug "Block data production has been interrupted" raise exc except CatchableError as exc: error "An unexpected error occurred while getting block data", error_name = exc.name, error_msg = exc.msg - return false + return withForkyMaybeBlindedBlck(maybeBlock): when isBlinded: @@ -282,7 +162,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, if notSlashable.isErr(): warn "Slashing protection activated for blinded block proposal" - return false + return let signature = try: @@ -292,7 +172,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, if res.isErr(): warn "Unable to sign blinded block proposal using remote signer", reason = res.error() - return false + return res.get() except CancelledError as exc: debug "Blinded block signature process has been interrupted" @@ -300,7 +180,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, except CatchableError as exc: error "An unexpected error occurred while signing blinded block", error_name = exc.name, error_msg = exc.msg - return false + return let signedBlock = @@ -313,7 +193,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, except ValidatorApiError as exc: warn "Unable to publish blinded block", reason = exc.getFailureReason() - return false + return except CancelledError as exc: debug "Blinded block publication has been interrupted" raise exc @@ -321,17 +201,15 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, error "An unexpected error occurred while publishing blinded " & "block", error_name = exc.name, error_msg = exc.msg - return false + return if res: let delay = vc.getDelay(slot.block_deadline()) beacon_blocks_sent.inc() beacon_blocks_sent_delay.observe(delay.toFloatSeconds()) notice "Blinded block published", delay = delay - true else: warn "Blinded block was not accepted by beacon node" - false else: let blockRoot = hash_tree_root( @@ -367,7 +245,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, if notSlashable.isErr(): warn "Slashing protection activated for block proposal" - return false + return let signature = @@ -377,7 +255,7 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, if res.isErr(): warn "Unable to sign block proposal using remote signer", reason = res.error() - return false + return res.get() except CancelledError as exc: debug "Block signature process has been interrupted" @@ -385,240 +263,12 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot, except CatchableError as exc: error "An unexpected error occurred while signing block", error_name = exc.name, error_msg = exc.msg - return false + return signedBlockContents = RestPublishedSignedBlockContents.init( forkyMaybeBlindedBlck, blockRoot, signature) - res = - try: - debug "Sending block" - await vc.publishBlock(signedBlockContents, ApiStrategyKind.First) - except ValidatorApiError as exc: - warn "Unable to publish block", reason = exc.getFailureReason() - return false - except CancelledError as exc: - debug "Block publication has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while publishing block", - error_name = exc.name, error_msg = exc.msg - return false - - if res: - let delay = vc.getDelay(slot.block_deadline()) - beacon_blocks_sent.inc() - beacon_blocks_sent_delay.observe(delay.toFloatSeconds()) - notice "Block published", delay = delay - true - else: - warn "Block was not accepted by beacon node" - false - -proc publishBlockV2(vc: ValidatorClientRef, currentSlot, slot: Slot, - fork: Fork, randaoReveal: ValidatorSig, - validator: AttachedValidator) {.async.} = - let - genesisRoot = vc.beaconGenesis.genesis_validators_root - graffiti = vc.getGraffitiBytes(validator) - vindex = validator.index.get() - - logScope: - validator = validatorLog(validator) - validator_index = vindex - slot = slot - wall_slot = currentSlot - - var beaconBlocks = - block: - let blindedBlockFut = - if vc.config.payloadBuilderEnable: - vc.produceBlindedBlock(currentSlot, slot, randaoReveal, graffiti, - validator) - else: - nil - let normalBlockFut = vc.produceBlock(currentSlot, slot, randaoReveal, - graffiti, validator) - let blindedBlock = - if isNil(blindedBlockFut): - Opt.none(PreparedBlindedBeaconBlock) - else: - try: - await blindedBlockFut - except CancelledError as exc: - if not(normalBlockFut.finished()): - await normalBlockFut.cancelAndWait() - raise exc - except CatchableError: - # This should not happen, because all the exceptions are handled. - Opt.none(PreparedBlindedBeaconBlock) - - let normalBlock = - if blindedBlock.isNone(): - try: - await normalBlockFut - except CancelledError as exc: - raise exc - except CatchableError: - # This should not happen, because all the exceptions are handled. - Opt.none(PreparedBeaconBlock) - else: - if not(normalBlockFut.finished()): - asyncSpawn lazyWait(normalBlockFut) - Opt.none(PreparedBeaconBlock) - - if blindedBlock.isNone() and normalBlock.isNone(): - return - - (blindedBlock: blindedBlock, normalBlock: normalBlock) - - if beaconBlocks.blindedBlock.isSome(): - let - preparedBlock = beaconBlocks.blindedBlock.get() - signingRoot = compute_block_signing_root(fork, genesisRoot, slot, - preparedBlock.blockRoot) - notSlashable = vc.attachedValidators[] - .slashingProtection - .registerBlock(vindex, validator.pubkey, slot, signingRoot) - - logScope: - blck = shortLog(preparedBlock.data) - block_root = shortLog(preparedBlock.blockRoot) - signing_root = shortLog(signingRoot) - - if notSlashable.isOk(): - let - signature = - try: - let res = await validator.getBlockSignature(fork, genesisRoot, - slot, - preparedBlock.blockRoot, - preparedBlock.data) - if res.isErr(): - warn "Unable to sign blinded block proposal using remote signer", - reason = res.error() - return - res.get() - except CancelledError as exc: - debug "Blinded block signature process has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while signing blinded block", - error_name = exc.name, error_msg = exc.msg - return - - logScope: - signature = shortLog(signature) - - let - signedBlock = ForkedSignedBlindedBeaconBlock.init(preparedBlock.data, - preparedBlock.blockRoot, signature) - res = - try: - debug "Sending blinded block" - await vc.publishBlindedBlock(signedBlock, ApiStrategyKind.First) - except ValidatorApiError as exc: - warn "Unable to publish blinded block", - reason = exc.getFailureReason() - return - except CancelledError as exc: - debug "Blinded block publication has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while publishing blinded block", - error_name = exc.name, error_msg = exc.msg - return - - if res: - let delay = vc.getDelay(slot.block_deadline()) - beacon_blocks_sent.inc() - beacon_blocks_sent_delay.observe(delay.toFloatSeconds()) - notice "Blinded block published", delay = delay - else: - warn "Blinded block was not accepted by beacon node" - else: - warn "Slashing protection activated for blinded block proposal" - else: - let - preparedBlock = beaconBlocks.normalBlock.get() - signingRoot = compute_block_signing_root(fork, genesisRoot, slot, - preparedBlock.blockRoot) - notSlashable = vc.attachedValidators[] - .slashingProtection - .registerBlock(vindex, validator.pubkey, slot, signingRoot) - - logScope: - blck = shortLog(preparedBlock.data) - block_root = shortLog(preparedBlock.blockRoot) - signing_root = shortLog(signingRoot) - - if notSlashable.isOk(): - let - signature = - try: - let res = await validator.getBlockSignature(fork, - genesisRoot, slot, - preparedBlock.blockRoot, - preparedBlock.data) - if res.isErr(): - warn "Unable to sign block proposal using remote signer", - reason = res.error() - return - res.get() - except CancelledError as exc: - debug "Block signature process has been interrupted" - raise exc - except CatchableError as exc: - error "An unexpected error occurred while signing block", - error_name = exc.name, error_msg = exc.msg - return - - signedBlockContents = - case preparedBlock.data.kind - of ConsensusFork.Phase0: - RestPublishedSignedBlockContents(kind: ConsensusFork.Phase0, - phase0Data: phase0.SignedBeaconBlock( - message: preparedBlock.data.phase0Data, - root: preparedBlock.blockRoot, - signature: signature)) - of ConsensusFork.Altair: - RestPublishedSignedBlockContents(kind: ConsensusFork.Altair, - altairData: altair.SignedBeaconBlock( - message: preparedBlock.data.altairData, - root: preparedBlock.blockRoot, - signature: signature)) - of ConsensusFork.Bellatrix: - RestPublishedSignedBlockContents(kind: ConsensusFork.Bellatrix, - bellatrixData: bellatrix.SignedBeaconBlock( - message: preparedBlock.data.bellatrixData, - root: preparedBlock.blockRoot, - signature: signature)) - of ConsensusFork.Capella: - RestPublishedSignedBlockContents(kind: ConsensusFork.Capella, - capellaData: capella.SignedBeaconBlock( - message: preparedBlock.data.capellaData, - root: preparedBlock.blockRoot, - signature: signature)) - of ConsensusFork.Deneb: - RestPublishedSignedBlockContents(kind: ConsensusFork.Deneb, - denebData: DenebSignedBlockContents( - signed_block: deneb.SignedBeaconBlock( - message: preparedBlock.data.denebData, - root: preparedBlock.blockRoot, - signature: signature), - kzg_proofs: preparedBlock.kzgProofsOpt.get, - blobs: preparedBlock.blobsOpt.get)) - of ConsensusFork.Electra: - RestPublishedSignedBlockContents(kind: ConsensusFork.Electra, - electraData: ElectraSignedBlockContents( - signed_block: electra.SignedBeaconBlock( - message: preparedBlock.data.electraData, - root: preparedBlock.blockRoot, - signature: signature), - kzg_proofs: preparedBlock.kzgProofsOpt.get, - blobs: preparedBlock.blobsOpt.get)) - res = try: debug "Sending block" @@ -641,8 +291,6 @@ proc publishBlockV2(vc: ValidatorClientRef, currentSlot, slot: Slot, notice "Block published", delay = delay else: warn "Block was not accepted by beacon node" - else: - warn "Slashing protection activated for block proposal" proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot, validator: AttachedValidator) {.async.} = @@ -677,18 +325,7 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot, error_name = exc.name, error_msg = exc.msg return - # TODO (cheatfate): This branch should be removed as soon as `produceBlockV2` - # call will be fully deprecated. - if vc.isProduceBlockV3Supported(): - # We call `V3` first, if call fails and `isProduceBlockV3Supported()` - # did not find any nodes which support `V3` we try to call `V2`. - let res = - await vc.publishBlockV3(currentSlot, slot, fork, randaoReveal, validator) - if not(res) and not(vc.isProduceBlockV3Supported()): - notice "Block production using V3 failed, trying V2" - await vc.publishBlockV2(currentSlot, slot, fork, randaoReveal, validator) - else: - await vc.publishBlockV2(currentSlot, slot, fork, randaoReveal, validator) + await vc.publishBlockV3(currentSlot, slot, fork, randaoReveal, validator) proc proposeBlock(vc: ValidatorClientRef, slot: Slot, proposerKey: ValidatorPubKey) {.async.} = diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index 971302085..40cf6a637 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -117,7 +117,6 @@ type RestBeaconNodeFeature* {.pure.} = enum NoNimbusExtensions, ## BN does not support Nimbus Extensions - NoProduceBlockV3 ## BN does not support produceBlockV3 call TimeOffset* = object value: int64 From dd452f71d38414a90815a695adc72cdfef15c877 Mon Sep 17 00:00:00 2001 From: tersec Date: Tue, 21 May 2024 23:59:11 +0000 Subject: [PATCH 2/8] rm outdated/semi-duplicate execution layer withdrawal request processing (#6301) --- beacon_chain/spec/state_transition_block.nim | 166 +++++++------------ ncli/ncli.nim | 4 + tests/test_beacon_validators.nim | 1 + 3 files changed, 69 insertions(+), 102 deletions(-) diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index c43722e30..850df60e9 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -507,43 +507,86 @@ proc process_bls_to_execution_change*( ok() -# https://github.com/ethereum/consensus-specs/blob/94a0b6c581f2809aa8aca4ef7ee6fbb63f9d74e9/specs/electra/beacon-chain.md#new-process_execution_layer_exit -func process_execution_layer_withdrawal_request( +# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.2/specs/electra/beacon-chain.md#new-process_execution_layer_withdrawal_request +func process_execution_layer_withdrawal_request*( cfg: RuntimeConfig, state: var electra.BeaconState, execution_layer_withdrawal_request: ExecutionLayerWithdrawalRequest, - exit_queue_info: ExitQueueInfo, cache: var StateCache): - Result[ExitQueueInfo, cstring] = - # Verify pubkey exists + cache: var StateCache) = let - pubkey_to_exit = execution_layer_withdrawal_request.validator_pubkey - validator_index = findValidatorIndex(state, pubkey_to_exit).valueOr: - return err("process_execution_layer_withdrawal_request: unknown index for validator pubkey") - validator = state.validators.item(validator_index) + amount = execution_layer_withdrawal_request.amount + is_full_exit_request = amount == static(FULL_EXIT_REQUEST_AMOUNT.Gwei) + + # If partial withdrawal queue is full, only full exits are processed + if lenu64(state.pending_partial_withdrawals) == + PENDING_PARTIAL_WITHDRAWALS_LIMIT and not is_full_exit_request: + return + + let + request_pubkey = execution_layer_withdrawal_request.validator_pubkey + index = findValidatorIndex(state, request_pubkey).valueOr: + return + validator = state.validators.item(index) # Verify withdrawal credentials let - is_execution_address = validator.has_eth1_withdrawal_credential + has_correct_credential = has_execution_withdrawal_credential(validator) is_correct_source_address = validator.withdrawal_credentials.data.toOpenArray(12, 31) == execution_layer_withdrawal_request.source_address.data - if not (is_execution_address and is_correct_source_address): - return err("process_execution_layer_withdrawal_request: not both execution address and correct source address") + + if not (has_correct_credential and is_correct_source_address): + return # Verify the validator is active if not is_active_validator(validator, get_current_epoch(state)): - return err("process_execution_layer_withdrawal_request: not active validator") + return # Verify exit has not been initiated if validator.exit_epoch != FAR_FUTURE_EPOCH: - return err("process_execution_layer_withdrawal_request: validator exit already initiated") + return # Verify the validator has been active long enough - if get_current_epoch(state) < validator.activation_epoch + cfg.SHARD_COMMITTEE_PERIOD: - return err("process_execution_layer_withdrawal_request: validator not active long enough") + if get_current_epoch(state) < + validator.activation_epoch + cfg.SHARD_COMMITTEE_PERIOD: + return - # Initiate exit - ok(? initiate_validator_exit( - cfg, state, validator_index, exit_queue_info, cache)) + let pending_balance_to_withdraw = + get_pending_balance_to_withdraw(state, index) + + if is_full_exit_request: + # Only exit validator if it has no pending withdrawals in the queue + if pending_balance_to_withdraw == 0.Gwei: + if initiate_validator_exit(cfg, state, index, default(ExitQueueInfo), + cache).isErr(): + return + return + + let + has_sufficient_effective_balance = + validator.effective_balance >= static(MIN_ACTIVATION_BALANCE.Gwei) + has_excess_balance = state.balances.item(index) > + static(MIN_ACTIVATION_BALANCE.Gwei) + pending_balance_to_withdraw + + # Only allow partial withdrawals with compounding withdrawal credentials + if has_compounding_withdrawal_credential(validator) and + has_sufficient_effective_balance and has_excess_balance: + let + to_withdraw = min( + state.balances.item(index) - static(MIN_ACTIVATION_BALANCE.Gwei) - + pending_balance_to_withdraw, + amount + ) + exit_queue_epoch = + compute_exit_epoch_and_update_churn(cfg, state, to_withdraw, cache) + withdrawable_epoch = + Epoch(exit_queue_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) + + # In theory can fail, but failing/early returning here is indistinguishable + discard state.pending_partial_withdrawals.add(PendingPartialWithdrawal( + index: index.uint64, + amount: to_withdraw, + withdrawable_epoch: withdrawable_epoch, + )) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#consolidations proc process_consolidation*( @@ -703,8 +746,8 @@ proc process_operations( # [New in Electra:EIP7002:EIP7251] when typeof(body).kind >= ConsensusFork.Electra: for op in body.execution_payload.withdrawal_requests: - discard ? process_execution_layer_withdrawal_request( - cfg, state, op, default(ExitQueueInfo), cache) + process_execution_layer_withdrawal_request( + cfg, state, op, cache) for op in body.execution_payload.deposit_receipts: debugComment "combine with previous bloom filter construction" let bloom_filter = constructBloomFilter(state.validators.asSeq) @@ -1035,87 +1078,6 @@ func process_withdrawals*( ok() -# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-process_execution_layer_withdrawal_request -func process_execution_layer_withdrawal_request*( - cfg: RuntimeConfig, state: var electra.BeaconState, - execution_layer_withdrawal_request: ExecutionLayerWithdrawalRequest, - cache: var StateCache) = - let - amount = execution_layer_withdrawal_request.amount - is_full_exit_request = amount == static(FULL_EXIT_REQUEST_AMOUNT.Gwei) - - # If partial withdrawal queue is full, only full exits are processed - if lenu64(state.pending_partial_withdrawals) == - PENDING_PARTIAL_WITHDRAWALS_LIMIT and not is_full_exit_request: - return - - let - request_pubkey = execution_layer_withdrawal_request.validator_pubkey - index = findValidatorIndex(state, request_pubkey).valueOr: - return - validator = state.validators.item(index) - - # Verify withdrawal credentials - let - has_correct_credential = has_execution_withdrawal_credential(validator) - is_correct_source_address = - validator.withdrawal_credentials.data.toOpenArray(12, 31) == - execution_layer_withdrawal_request.source_address.data - - if not (has_correct_credential and is_correct_source_address): - return - - # Verify the validator is active - if not is_active_validator(validator, get_current_epoch(state)): - return - - # Verify exit has not been initiated - if validator.exit_epoch != FAR_FUTURE_EPOCH: - return - - # Verify the validator has been active long enough - if get_current_epoch(state) < - validator.activation_epoch + cfg.SHARD_COMMITTEE_PERIOD: - return - - let pending_balance_to_withdraw = - get_pending_balance_to_withdraw(state, index) - - if is_full_exit_request: - # Only exit validator if it has no pending withdrawals in the queue - if pending_balance_to_withdraw == 0.Gwei: - if initiate_validator_exit(cfg, state, index, default(ExitQueueInfo), - cache).isErr(): - return - return - - let - has_sufficient_effective_balance = - validator.effective_balance >= static(MIN_ACTIVATION_BALANCE.Gwei) - has_excess_balance = state.balances.item(index) > - static(MIN_ACTIVATION_BALANCE.Gwei) + pending_balance_to_withdraw - - # Only allow partial withdrawals with compounding withdrawal credentials - if has_compounding_withdrawal_credential(validator) and - has_sufficient_effective_balance and has_excess_balance: - let - to_withdraw = min( - state.balances.item(index) - static(MIN_ACTIVATION_BALANCE.Gwei) - - pending_balance_to_withdraw, - amount - ) - exit_queue_epoch = - compute_exit_epoch_and_update_churn(cfg, state, to_withdraw, cache) - withdrawable_epoch = - Epoch(exit_queue_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) - - # In theory can fail, but failing/early returning here is indistinguishable - discard state.pending_partial_withdrawals.add(PendingPartialWithdrawal( - index: index.uint64, - amount: to_withdraw, - withdrawable_epoch: withdrawable_epoch, - )) - # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/deneb/beacon-chain.md#kzg_commitment_to_versioned_hash func kzg_commitment_to_versioned_hash*( kzg_commitment: KzgCommitment): VersionedHash = diff --git a/ncli/ncli.nim b/ncli/ncli.nim index 25d3dd123..cbb675bf6 100644 --- a/ncli/ncli.nim +++ b/ncli/ncli.nim @@ -239,16 +239,19 @@ proc doSSZ(conf: NcliConf) = of "bellatrix_signed_block": printit(bellatrix.SignedBeaconBlock) of "capella_signed_block": printit(capella.SignedBeaconBlock) of "deneb_signed_block": printit(deneb.SignedBeaconBlock) + of "electra_signed_block": printit(electra.SignedBeaconBlock) of "phase0_block": printit(phase0.BeaconBlock) of "altair_block": printit(altair.BeaconBlock) of "bellatrix_block": printit(bellatrix.BeaconBlock) of "capella_block": printit(capella.BeaconBlock) of "deneb_block": printit(deneb.BeaconBlock) + of "electra_block": printit(electra.BeaconBlock) of "phase0_block_body": printit(phase0.BeaconBlockBody) of "altair_block_body": printit(altair.BeaconBlockBody) of "bellatrix_block_body": printit(bellatrix.BeaconBlockBody) of "capella_block_body": printit(capella.BeaconBlockBody) of "deneb_block_body": printit(deneb.BeaconBlockBody) + of "electra_block_body": printit(electra.BeaconBlockBody) of "block_header": printit(BeaconBlockHeader) of "deposit": printit(Deposit) of "deposit_data": printit(DepositData) @@ -258,6 +261,7 @@ proc doSSZ(conf: NcliConf) = of "bellatrix_state": printit(bellatrix.BeaconState) of "capella_state": printit(capella.BeaconState) of "deneb_state": printit(deneb.BeaconState) + of "electra_state": printit(electra.BeaconState) of "proposer_slashing": printit(ProposerSlashing) of "voluntary_exit": printit(VoluntaryExit) diff --git a/tests/test_beacon_validators.nim b/tests/test_beacon_validators.nim index 3a376a136..9d72b60f3 100644 --- a/tests/test_beacon_validators.nim +++ b/tests/test_beacon_validators.nim @@ -6,6 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. {.push raises: [].} +{.used.} import unittest2, results, chronos, stint import ../beacon_chain/validators/beacon_validators, From 3e6990ccc4897498125cac2826567c72663941b3 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 22 May 2024 02:52:35 +0000 Subject: [PATCH 3/8] rm unused ProduceBlockResponseV2 type (#6302) --- .../eth2_apis/eth2_rest_serialization.nim | 122 +----------------- beacon_chain/spec/eth2_apis/rest_types.nim | 33 ----- tests/test_signing_node.nim | 58 ++++----- 3 files changed, 32 insertions(+), 181 deletions(-) diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index 5e518d274..e9d5edac5 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -403,8 +403,7 @@ type RestNimbusTimestamp2 | GetGraffitiResponse - DecodeConsensysTypes* = - ProduceBlockResponseV2 | ProduceBlindedBlockResponse + DecodeConsensysTypes* = ProduceBlindedBlockResponse RestVersioned*[T] = object data*: T @@ -1423,8 +1422,7 @@ template prepareForkedBlockReading(blockType: typedesc, if version.isNone(): reader.raiseUnexpectedValue("Incorrect version field value") of "data": - when (blockType is ProduceBlockResponseV2) or - (blockType is ForkedBlindedBeaconBlock) or + when (blockType is ForkedBlindedBeaconBlock) or (blockType is ProduceBlockResponseV3): if data.isSome(): reader.raiseUnexpectedField( @@ -1472,92 +1470,6 @@ template prepareForkedBlockReading(blockType: typedesc, if data.isNone(): reader.raiseUnexpectedValue("Field `data` is missing") -proc readValue*[BlockType: ProduceBlockResponseV2]( - reader: var JsonReader[RestJson], - value: var BlockType) {.raises: [IOError, SerializationError].} = - var - version: Opt[ConsensusFork] - data: Opt[JsonString] - blinded: Opt[bool] - payloadValue: Opt[Uint256] - blockValue: Opt[Uint256] - - prepareForkedBlockReading(BlockType, reader, version, data, blinded, - payloadValue, blockValue) - - case version.get(): - of ConsensusFork.Phase0: - let res = - try: - RestJson.decode(string(data.get()), - phase0.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect phase0 block format") - value = ProduceBlockResponseV2(kind: ConsensusFork.Phase0, - phase0Data: res) - of ConsensusFork.Altair: - let res = - try: - RestJson.decode(string(data.get()), - altair.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect altair block format") - - value = ProduceBlockResponseV2(kind: ConsensusFork.Altair, - altairData: res) - of ConsensusFork.Bellatrix: - let res = - try: - RestJson.decode(string(data.get()), - bellatrix.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect bellatrix block format") - - value = ProduceBlockResponseV2(kind: ConsensusFork.Bellatrix, - bellatrixData: res) - of ConsensusFork.Capella: - let res = - try: - RestJson.decode(string(data.get()), - capella.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect capella block format") - - value = ProduceBlockResponseV2(kind: ConsensusFork.Capella, - capellaData: res) - of ConsensusFork.Deneb: - let res = - try: - RestJson.decode(string(data.get()), - deneb.BlockContents, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect deneb block format") - - value = ProduceBlockResponseV2(kind: ConsensusFork.Deneb, - denebData: res) - of ConsensusFork.Electra: - let res = - try: - RestJson.decode(string(data.get()), - electra.BlockContents, - requireAllFields = true, - allowUnknownFields = true) - except SerializationError: - reader.raiseUnexpectedValue("Incorrect electra block format") - - value = ProduceBlockResponseV2(kind: ConsensusFork.Electra, - electraData: res) - proc readValue*[BlockType: ForkedBlindedBeaconBlock]( reader: var JsonReader[RestJson], value: var BlockType @@ -3846,35 +3758,7 @@ proc decodeBytes*[T: DecodeConsensysTypes]( data = string.fromBytes(value) return err("Serialization error") elif mediaType == OctetStreamMediaType: - when t is ProduceBlockResponseV2: - let fork = ConsensusFork.decodeString(consensusVersion).valueOr: - return err("Invalid or Unsupported consensus version") - case fork - of ConsensusFork.Electra: - let blckContents = ? readSszResBytes(electra.BlockContents, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Electra, - electraData: blckContents)) - of ConsensusFork.Deneb: - let blckContents = ? readSszResBytes(deneb.BlockContents, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Deneb, - denebData: blckContents)) - of ConsensusFork.Capella: - let blck = ? readSszResBytes(capella.BeaconBlock, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Capella, - capellaData: blck)) - of ConsensusFork.Bellatrix: - let blck = ? readSszResBytes(bellatrix.BeaconBlock, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Bellatrix, - bellatrixData: blck)) - of ConsensusFork.Altair: - let blck = ? readSszResBytes(altair.BeaconBlock, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Altair, - altairData: blck)) - of ConsensusFork.Phase0: - let blck = ? readSszResBytes(phase0.BeaconBlock, value) - ok(ProduceBlockResponseV2(kind: ConsensusFork.Phase0, - phase0Data: blck)) - elif t is ProduceBlindedBlockResponse: + when t is ProduceBlindedBlockResponse: let fork = ConsensusFork.decodeString(consensusVersion).valueOr: return err("Invalid or Unsupported consensus version") case fork diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 13c9ed7fd..da21e5b21 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -357,15 +357,6 @@ type of ConsensusFork.Deneb: denebBody*: deneb.BeaconBlockBody of ConsensusFork.Electra: electraBody*: electra.BeaconBlockBody - ProduceBlockResponseV2* = object - case kind*: ConsensusFork - of ConsensusFork.Phase0: phase0Data*: phase0.BeaconBlock - of ConsensusFork.Altair: altairData*: altair.BeaconBlock - of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.BeaconBlock - of ConsensusFork.Capella: capellaData*: capella.BeaconBlock - of ConsensusFork.Deneb: denebData*: deneb.BlockContents - of ConsensusFork.Electra: electraData*: electra.BlockContents - ProduceBlockResponseV3* = ForkedMaybeBlindedBeaconBlock VCRuntimeConfig* = Table[string, string] @@ -1058,27 +1049,3 @@ func toValidatorIndex*(value: RestValidatorIndex): Result[ValidatorIndex, err(ValidatorIndexError.TooHighValue) else: doAssert(false, "ValidatorIndex type size is incorrect") - -template withBlck*(x: ProduceBlockResponseV2, - body: untyped): untyped = - case x.kind - of ConsensusFork.Phase0: - const consensusFork {.inject, used.} = ConsensusFork.Phase0 - template blck: untyped {.inject.} = x.phase0Data - body - of ConsensusFork.Altair: - const consensusFork {.inject, used.} = ConsensusFork.Altair - template blck: untyped {.inject.} = x.altairData - body - of ConsensusFork.Bellatrix: - const consensusFork {.inject, used.} = ConsensusFork.Bellatrix - template blck: untyped {.inject.} = x.bellatrixData - body - of ConsensusFork.Capella: - const consensusFork {.inject, used.} = ConsensusFork.Capella - template blck: untyped {.inject.} = x.capellaData - body - of ConsensusFork.Deneb: - const consensusFork {.inject, used.} = ConsensusFork.Deneb - template blck: untyped {.inject.} = x.denebData.blck - body diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index 325e29cf8..06453545c 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -6,10 +6,10 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. {.push raises: [].} +{.used.} import - std/algorithm, - unittest2, chronicles, stew/[results, byteutils, io2], + unittest2, chronicles, results, stew/[byteutils, io2], chronos/asyncproc, chronos/unittest2/asynctests, ../beacon_chain/spec/[signatures, crypto], @@ -19,8 +19,6 @@ import from std/os import getEnv, osErrorMsg -{.used.} - const TestDirectoryName = "test-signing-node" TestDirectoryNameVerifyingWeb3Signer = "test-signing-node-verifying-web3signer" @@ -63,8 +61,8 @@ const AgAttestation = "{\"data\":{\"aggregation_bits\":\"0x01\",\"signature\":\"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505\",\"data\":{\"slot\":\"1\",\"index\":\"1\",\"beacon_block_root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\",\"source\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"},\"target\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"}}}}" - CapellaBlock = "{\"version\":\"capella\",\"data\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[]},\"bls_to_execution_changes\":[]}}}" - DenebBlockContents = "{\"version\":\"deneb\",\"data\":{\"block\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[],\"blob_gas_used\":\"2316131761\",\"excess_blob_gas\":\"231613172261\"},\"bls_to_execution_changes\":[],\"blob_kzg_commitments\":[]}},\"kzg_proofs\":[],\"blobs\":[]}}" + CapellaBlock = "{\"message\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[]},\"bls_to_execution_changes\":[]}},\"signature\":\"$2\"}" + DenebBlockContents = "{\"signed_block\":{\"message\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[],\"blob_gas_used\":\"2316131761\",\"excess_blob_gas\":\"231613172261\"},\"bls_to_execution_changes\":[],\"blob_kzg_commitments\":[]}},\"signature\":\"$2\"},\"kzg_proofs\":[],\"blobs\":[]}" SigningNodeAddress = "127.0.0.1" defaultSigningNodePort = 35333 @@ -84,14 +82,15 @@ func getNodePort(basePort: int, rt: RemoteSignerType): int = of RemoteSignerType.VerifyingWeb3Signer: basePort + 1 -func init(T: type ForkedBeaconBlock, contents: ProduceBlockResponseV2): T = +func init( + T: type ForkedBeaconBlock, contents: RestPublishedSignedBlockContents): T = case contents.kind of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix: raiseAssert "Unsupported fork" of ConsensusFork.Capella: - return ForkedBeaconBlock.init(contents.capellaData) + return ForkedBeaconBlock.init(contents.capellaData.message) of ConsensusFork.Deneb: - return ForkedBeaconBlock.init(contents.denebData.`block`) + return ForkedBeaconBlock.init(contents.denebData.signed_block.message) of ConsensusFork.Electra: debugComment "probably like the deneb case" return default(T) @@ -100,27 +99,26 @@ proc getBlock( fork: ConsensusFork, feeRecipient = SigningExpectedFeeRecipient ): ForkedBeaconBlock {.raises: [ResultError[cstring]].} = - let - blckData = - try: - case fork - of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix: raiseAssert "Unsupported fork" - of ConsensusFork.Capella: CapellaBlock % [feeRecipient] - of ConsensusFork.Deneb: DenebBlockContents % [feeRecipient] - of ConsensusFork.Electra: - debugComment "electra test signing node getblock" - raiseAssert "electra unsupported" - except ValueError: - # https://github.com/nim-lang/Nim/pull/23356 - raiseAssert "Arguments match the format string" - contentType = ContentTypeData( - mediaType: MediaType.init("application/json")) + let blckData = + try: + case fork + of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix: + raiseAssert "Unsupported fork" + of ConsensusFork.Capella: CapellaBlock % [feeRecipient, SomeSignature] + of ConsensusFork.Deneb: + DenebBlockContents % [feeRecipient, SomeSignature] + of ConsensusFork.Electra: + debugComment "electra test signing node getblock" + raiseAssert "electra unsupported" + except ValueError: + # https://github.com/nim-lang/Nim/pull/23356 + raiseAssert "Arguments match the format string" - let b = decodeBytes(ProduceBlockResponseV2, - blckData.toOpenArrayByte(0, len(blckData) - 1), - Opt.some(contentType), - $fork).tryGet() - ForkedBeaconBlock.init(b) + try: + ForkedBeaconBlock.init(RestJson.decode( + blckData, RestPublishedSignedBlockContents)) + except SerializationError: + raiseAssert "malformed block contents" func init(t: typedesc[Web3SignerForkedBeaconBlock], forked: ForkedBeaconBlock): Web3SignerForkedBeaconBlock = @@ -371,6 +369,8 @@ let fatal "Invalid base port arg", basePort = basePortStr, exc = exc.msg quit 1 +from std/algorithm import sorted + block: let res = createTestDir(RemoteSignerType.Web3Signer) doAssert(res.isOk()) From 708b23f08441bda4e11bd9becfac1340d18b8ea8 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 22 May 2024 05:47:03 +0000 Subject: [PATCH 4/8] rm unused produceBlockV2Plain() RPC signature (#6304) --- beacon_chain/spec/eth2_apis/rest_validator_calls.nim | 9 --------- 1 file changed, 9 deletions(-) diff --git a/beacon_chain/spec/eth2_apis/rest_validator_calls.nim b/beacon_chain/spec/eth2_apis/rest_validator_calls.nim index 7ee76bb68..d12b22152 100644 --- a/beacon_chain/spec/eth2_apis/rest_validator_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_validator_calls.nim @@ -36,15 +36,6 @@ proc getSyncCommitteeDutiesPlain*( meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Validator/getSyncCommitteeDuties -proc produceBlockV2Plain*( - slot: Slot, - randao_reveal: ValidatorSig, - graffiti: GraffitiBytes - ): RestPlainResponse {. - rest, endpoint: "/eth/v2/validator/blocks/{slot}", - accept: preferSSZ, meth: MethodGet.} - ## https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2 - proc produceBlockV3Plain*( slot: Slot, randao_reveal: ValidatorSig, From e0f8ea752b4a55db2c4616bf7ef4d8401a55f6c8 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 22 May 2024 10:21:04 +0200 Subject: [PATCH 5/8] era: speed up index reading (#6305) --- ncli/era.nim | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/ncli/era.nim b/ncli/era.nim index e7dba5e93..6f27134b2 100644 --- a/ncli/era.nim +++ b/ncli/era.nim @@ -92,41 +92,43 @@ proc appendRecord(f: IoHandle, index: Index): Result[int64, string] = f.appendIndex(index.startSlot, index.offsets) proc readIndex*(f: IoHandle): Result[Index, string] = + var + buf: seq[byte] + pos: int + let startPos = ? f.getFilePos().mapErr(toString) fileSize = ? f.getFileSize().mapErr(toString) - header = ? f.readHeader() + header = ? f.readRecord(buf) if header.typ != E2Index: return err("not an index") - if header.len < 16: return err("index entry too small") - if header.len mod 8 != 0: return err("index length invalid") + if buf.len < 16: return err("index entry too small") + if buf.len mod 8 != 0: return err("index length invalid") - var buf: array[8, byte] - ? f.readFileExact(buf) let - slot = uint64.fromBytesLE(buf) - count = header.len div 8 - 2 + slot = uint64.fromBytesLE(buf.toOpenArray(pos, pos + 7)) + count = buf.len div 8 - 2 + pos += 8 + + # technically not an error, but we'll throw this sanity check in here.. + if slot > int32.high().uint64: return err("fishy slot") var offsets = newSeqUninitialized[int64](count) for i in 0.. fileSize: return err("Invalid offset") + if absolute < 0 or absolute > fileSize: return err("invalid offset") offsets[i] = absolute + pos += 8 - ? f.readFileExact(buf) - if uint64(count) != uint64.fromBytesLE(buf): return err("invalid count") - - # technically not an error, but we'll throw this sanity check in here.. - if slot > int32.high().uint64: return err("fishy slot") + if uint64(count) != uint64.fromBytesLE(buf.toOpenArray(pos, pos + 7)): + return err("invalid count") ok(Index(startSlot: Slot(slot), offsets: offsets)) From b56a6711220097f7b0485c4441de39ad55879f71 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 22 May 2024 11:56:37 +0000 Subject: [PATCH 6/8] fix most ConvFromXtoItselfNotNeeded hints and unhide remaining ones (#6307) --- .../consensus_object_pools/block_dag.nim | 6 +- beacon_chain/el/deposit_contract.nim | 4 +- beacon_chain/networking/eth2_discovery.nim | 4 +- beacon_chain/nim.cfg | 3 +- beacon_chain/rpc/rest_key_management_api.nim | 2 +- beacon_chain/rpc/rest_validator_api.nim | 8 +-- beacon_chain/spec/beaconstate.nim | 2 +- .../eth2_apis/rest_light_client_calls.nim | 6 +- beacon_chain/spec/keystore.nim | 62 +++++++++---------- beacon_chain/spec/state_transition_block.nim | 2 +- beacon_chain/sync/sync_manager.nim | 2 +- beacon_chain/validator_client/common.nim | 2 +- ncli/ncli_common.nim | 9 ++- ncli/ncli_testnet.nim | 6 +- ncli/nim.cfg | 1 - research/block_sim.nim | 3 +- research/nim.cfg | 1 - tests/nim.cfg | 3 +- tests/test_blockchain_dag.nim | 3 +- tests/test_sync_committee_pool.nim | 2 +- tests/test_sync_manager.nim | 8 +-- 21 files changed, 64 insertions(+), 75 deletions(-) diff --git a/beacon_chain/consensus_object_pools/block_dag.nim b/beacon_chain/consensus_object_pools/block_dag.nim index d8fb44074..1b126de43 100644 --- a/beacon_chain/consensus_object_pools/block_dag.nim +++ b/beacon_chain/consensus_object_pools/block_dag.nim @@ -9,12 +9,8 @@ import chronicles, - ../spec/datatypes/[phase0, altair, bellatrix], ../spec/forks -from ../spec/datatypes/capella import SomeBeaconBlock, TrustedBeaconBlock -from ../spec/datatypes/deneb import SomeBeaconBlock, TrustedBeaconBlock - export chronicles, forks type @@ -76,7 +72,7 @@ func init*( deneb.SomeBeaconBlock | deneb.TrustedBeaconBlock | electra.SomeBeaconBlock | electra.TrustedBeaconBlock): BlockRef = BlockRef.init( - root, Opt.some Eth2Digest(blck.body.execution_payload.block_hash), + root, Opt.some blck.body.execution_payload.block_hash, executionValid = executionValid or blck.body.execution_payload.block_hash == ZERO_HASH, blck.slot) diff --git a/beacon_chain/el/deposit_contract.nim b/beacon_chain/el/deposit_contract.nim index 445a3c579..213907879 100644 --- a/beacon_chain/el/deposit_contract.nim +++ b/beacon_chain/el/deposit_contract.nim @@ -137,7 +137,7 @@ proc sendEth(web3: Web3, to: Eth1Address, valueEth: int): Future[TxHash] = # TODO: Force json-rpc to generate 'data' field # should not be needed anymore, new execution-api schema # is using `input` field - data: some(newSeq[byte]()), + data: some(newSeq[byte]()), gas: Quantity(3000000).some, gasPrice: Quantity(1).some, value: some(valueEth.u256 * 1000000000000000000.u256), @@ -273,7 +273,7 @@ proc main() {.async.} = error "Failed to read an Eth1 private key from standard input" if privateKey.len > 0: - conf.privateKey = privateKey.string + conf.privateKey = privateKey let web3 = await initWeb3(conf.web3Url, conf.privateKey) diff --git a/beacon_chain/networking/eth2_discovery.nim b/beacon_chain/networking/eth2_discovery.nim index cd348a116..a8887aaf0 100644 --- a/beacon_chain/networking/eth2_discovery.nim +++ b/beacon_chain/networking/eth2_discovery.nim @@ -26,10 +26,10 @@ type func parseBootstrapAddress*(address: string): Result[enr.Record, cstring] = - let lowerCaseAddress = toLowerAscii(string address) + let lowerCaseAddress = toLowerAscii(address) if lowerCaseAddress.startsWith("enr:"): var enrRec: enr.Record - if enrRec.fromURI(string address): + if enrRec.fromURI(address): return ok enrRec return err "Invalid ENR bootstrap record" elif lowerCaseAddress.startsWith("enode:"): diff --git a/beacon_chain/nim.cfg b/beacon_chain/nim.cfg index 18b6891c8..83faae9b0 100644 --- a/beacon_chain/nim.cfg +++ b/beacon_chain/nim.cfg @@ -1,5 +1,5 @@ # beacon_chain -# Copyright (c) 2021-2023 Status Research & Development GmbH +# Copyright (c) 2021-2024 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). @@ -10,5 +10,4 @@ --styleCheck:usages --styleCheck:hint ---hint[ConvFromXtoItselfNotNeeded]:off --hint[Processing]:off diff --git a/beacon_chain/rpc/rest_key_management_api.nim b/beacon_chain/rpc/rest_key_management_api.nim index 1328e0817..ca4e78966 100644 --- a/beacon_chain/rpc/rest_key_management_api.nim +++ b/beacon_chain/rpc/rest_key_management_api.nim @@ -46,7 +46,7 @@ proc listRemoteValidators*( if item.kind == ValidatorKind.Remote and item.data.remotes.len == 1: validators.add RemoteKeystoreInfo( pubkey: item.pubkey, - url: HttpHostUri(item.data.remotes[0].url) + url: item.data.remotes[0].url ) validators diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index 545994ecf..1c6250915 100644 --- a/beacon_chain/rpc/rest_validator_api.nim +++ b/beacon_chain/rpc/rest_validator_api.nim @@ -688,8 +688,8 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = message = (await PayloadType.makeBeaconBlockForHeadAndSlot( node, qrandao, proposer, qgraffiti, qhead, qslot)).valueOr: return RestApiResponse.jsonError(Http500, error) - executionValue = Opt.some(UInt256(message.executionPayloadValue)) - consensusValue = Opt.some(UInt256(message.consensusBlockValue)) + executionValue = Opt.some(message.executionPayloadValue) + consensusValue = Opt.some(message.consensusBlockValue) headers = consensusFork.getMaybeBlindedHeaders( isBlinded = false, executionValue, consensusValue) @@ -914,7 +914,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = request.validator_index).pubkey node.validatorMonitor[].addAutoMonitor( - validator_pubkey, ValidatorIndex(request.validator_index)) + validator_pubkey, request.validator_index) RestApiResponse.jsonMsgResponse(BeaconCommitteeSubscriptionSuccess) @@ -955,7 +955,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = validator_pubkey, item.until_epoch) node.validatorMonitor[].addAutoMonitor( - validator_pubkey, ValidatorIndex(item.validator_index)) + validator_pubkey, item.validator_index) RestApiResponse.jsonMsgResponse(SyncCommitteeSubscriptionSuccess) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 2de5843fd..51e7f4b11 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -289,7 +289,7 @@ func initiate_validator_exit*( # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch validator.withdrawable_epoch = - Epoch(validator.exit_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) + validator.exit_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY if validator.withdrawable_epoch < validator.exit_epoch: return err("Invalid large withdrawable epoch") state.validators.mitem(index) = validator diff --git a/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim b/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim index 69d9ae337..78c5fa7ec 100644 --- a/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim @@ -212,7 +212,7 @@ proc getLightClientBootstrap*( let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: - raiseRestDecodingBytesError(cstring(consensusForkRes.error)) + raiseRestDecodingBytesError(consensusForkRes.error) ForkedLightClientBootstrap.decodeHttpLightClientObject( data, resp.contentType, consensusForkRes.get, cfg) of 404: @@ -294,7 +294,7 @@ proc getLightClientFinalityUpdate*( let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: - raiseRestDecodingBytesError(cstring(consensusForkRes.error)) + raiseRestDecodingBytesError(consensusForkRes.error) ForkedLightClientFinalityUpdate.decodeHttpLightClientObject( data, resp.contentType, consensusForkRes.get, cfg) of 404: @@ -336,7 +336,7 @@ proc getLightClientOptimisticUpdate*( let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: - raiseRestDecodingBytesError(cstring(consensusForkRes.error)) + raiseRestDecodingBytesError(consensusForkRes.error) ForkedLightClientOptimisticUpdate.decodeHttpLightClientObject( data, resp.contentType, consensusForkRes.get, cfg) of 404: diff --git a/beacon_chain/spec/keystore.nim b/beacon_chain/spec/keystore.nim index de09cca30..636c8faca 100644 --- a/beacon_chain/spec/keystore.nim +++ b/beacon_chain/spec/keystore.nim @@ -291,7 +291,7 @@ template `$`*(x: WalletName): string = # TODO: `burnMem` in nimcrypto could use distinctBase # to make its usage less error-prone. template burnMem*(m: var (Mnemonic|string)) = - ncrutils.burnMem(string m) + ncrutils.burnMem(distinctBase m) template burnMem*(m: var KeySeed) = ncrutils.burnMem(distinctBase m) @@ -324,7 +324,7 @@ const englishWordsDigest = "AD90BF3BEB7B0EB7E5ACD74727DC0DA96E0A280A258354E7293FB7E211AC03DB".toDigest -proc checkEnglishWords(): bool = +func checkEnglishWords(): bool = if len(englishWords) != wordListLen: false else: @@ -341,7 +341,7 @@ func validateKeyPath*(path: string): Result[KeyPath, cstring] = var digitCount: int var number: BiggestUInt try: - for elem in path.string.split("/"): + for elem in path.split("/"): # TODO: doesn't "m" have to be the first character and is it the only # place where it is valid? if elem == "m": @@ -382,7 +382,7 @@ func isControlRune(r: Rune): bool = let r = int r (r >= 0 and r < 0x20) or (r >= 0x7F and r < 0xA0) -proc init*(T: type KeystorePass, input: string): T = +func init*(T: type KeystorePass, input: string): T = for rune in toNFKD(input): if not isControlRune(rune): result.str.add rune @@ -395,7 +395,7 @@ func getSeed*(mnemonic: Mnemonic, password: KeystorePass): KeySeed = template add(m: var Mnemonic, s: cstring) = m.string.add s -proc generateMnemonic*( +func generateMnemonic*( rng: var HmacDrbgContext, words: openArray[cstring] = englishWords, entropyParam: openArray[byte] = @[]): Mnemonic = @@ -429,12 +429,12 @@ proc generateMnemonic*( result.add " " result.add words[entropy.getBitsBE(firstBit..lastBit)] -proc cmpIgnoreCase(lhs: cstring, rhs: string): int = +func cmpIgnoreCase(lhs: cstring, rhs: string): int = # TODO: This is a bit silly. # Nim should have a `cmp` function for C strings. cmpIgnoreCase($lhs, rhs) -proc validateMnemonic*(inputWords: string, +func validateMnemonic*(inputWords: string, outputMnemonic: var Mnemonic): bool = ## Accept a case-insensitive input string and returns `true` ## if it represents a valid mnenomic. The `outputMnemonic` @@ -465,7 +465,7 @@ proc validateMnemonic*(inputWords: string, return true -proc deriveChildKey*(parentKey: ValidatorPrivKey, +func deriveChildKey*(parentKey: ValidatorPrivKey, index: Natural): ValidatorPrivKey = let success = derive_child_secretKey(SecretKey result, SecretKey parentKey, @@ -475,7 +475,7 @@ proc deriveChildKey*(parentKey: ValidatorPrivKey, # into asserts inside the function. doAssert success -proc deriveMasterKey*(seed: KeySeed): ValidatorPrivKey = +func deriveMasterKey*(seed: KeySeed): ValidatorPrivKey = let success = derive_master_secretKey(SecretKey result, seq[byte] seed) # TODO `derive_master_secretKey` is reporting pre-condition @@ -483,17 +483,17 @@ proc deriveMasterKey*(seed: KeySeed): ValidatorPrivKey = # into asserts inside the function. doAssert success -proc deriveMasterKey*(mnemonic: Mnemonic, +func deriveMasterKey*(mnemonic: Mnemonic, password: KeystorePass): ValidatorPrivKey = deriveMasterKey(getSeed(mnemonic, password)) -proc deriveChildKey*(masterKey: ValidatorPrivKey, +func deriveChildKey*(masterKey: ValidatorPrivKey, path: KeyPath): ValidatorPrivKey = result = masterKey for idx in pathNodes(path): result = deriveChildKey(result, idx) -proc deriveChildKey*(masterKey: ValidatorPrivKey, +func deriveChildKey*(masterKey: ValidatorPrivKey, path: openArray[Natural]): ValidatorPrivKey = result = masterKey for idx in path: @@ -503,12 +503,12 @@ proc deriveChildKey*(masterKey: ValidatorPrivKey, # if we fail we want to scrub secrets from memory result = deriveChildKey(result, idx) -proc keyFromPath*(mnemonic: Mnemonic, +func keyFromPath*(mnemonic: Mnemonic, password: KeystorePass, path: KeyPath): ValidatorPrivKey = deriveChildKey(deriveMasterKey(mnemonic, password), path) -proc shaChecksum(key, cipher: openArray[byte]): Sha256Digest = +func shaChecksum(key, cipher: openArray[byte]): Sha256Digest = var ctx: sha256 ctx.init() ctx.update(key) @@ -681,7 +681,7 @@ proc readValue*(r: var JsonReader[DefaultFlavor], value: var Kdf) readValueImpl(r, value) {.pop.} -proc readValue*(r: var JsonReader, value: var (Checksum|Cipher|Kdf)) = +func readValue*(r: var JsonReader, value: var (Checksum|Cipher|Kdf)) = static: raiseAssert "Unknown flavor `JsonReader[" & $typeof(r).Flavor & "]` for `readValue` of `" & $typeof(value) & "`" @@ -951,7 +951,7 @@ func areValid(params: ScryptParams): bool = params.p == scryptParams.p and params.salt.bytes.len > 0 -proc decryptCryptoField*(crypto: Crypto, decKey: openArray[byte], +func decryptCryptoField*(crypto: Crypto, decKey: openArray[byte], outSecret: var seq[byte]): DecryptionStatus = if crypto.cipher.message.bytes.len == 0: return DecryptionStatus.InvalidKeystore @@ -977,7 +977,7 @@ proc decryptCryptoField*(crypto: Crypto, decKey: openArray[byte], aesCipher.clear() DecryptionStatus.Success -proc getDecryptionKey*(crypto: Crypto, password: KeystorePass, +func getDecryptionKey*(crypto: Crypto, password: KeystorePass, decKey: var seq[byte]): DecryptionStatus = let res = case crypto.kdf.function @@ -996,7 +996,7 @@ proc getDecryptionKey*(crypto: Crypto, password: KeystorePass, decKey = res DecryptionStatus.Success -proc decryptCryptoField*(crypto: Crypto, +func decryptCryptoField*(crypto: Crypto, password: KeystorePass, outSecret: var seq[byte]): DecryptionStatus = # https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition @@ -1027,7 +1027,7 @@ template parseRemoteKeystore*(jsonContent: string): RemoteKeystore = requireAllFields = false, allowUnknownFields = true) -proc getSaltKey(keystore: Keystore, password: KeystorePass): KdfSaltKey = +func getSaltKey(keystore: Keystore, password: KeystorePass): KdfSaltKey = let digest = case keystore.crypto.kdf.function of kdfPbkdf2: @@ -1050,8 +1050,8 @@ proc getSaltKey(keystore: Keystore, password: KeystorePass): KdfSaltKey = h.update(toBytesLE(uint64(params.r))) KdfSaltKey(digest.data) -proc `==`*(a, b: KdfSaltKey): bool {.borrow.} -proc hash*(salt: KdfSaltKey): Hash {.borrow.} +func `==`*(a, b: KdfSaltKey): bool {.borrow.} +func hash*(salt: KdfSaltKey): Hash {.borrow.} {.push warning[ProveField]:off.} func `==`*(a, b: Kdf): bool = @@ -1089,7 +1089,7 @@ func init*(t: typedesc[KeystoreCacheRef], expireTime: expireTime ) -proc clear*(cache: KeystoreCacheRef) = +func clear*(cache: KeystoreCacheRef) = cache.table.clear() proc pruneExpiredKeys*(cache: KeystoreCacheRef) = @@ -1110,7 +1110,7 @@ proc init*(t: typedesc[KeystoreCacheItem], keystore: Keystore, cipher: keystore.crypto.cipher, decryptionKey: @key, timestamp: Moment.now()) -proc getCachedKey*(cache: KeystoreCacheRef, +func getCachedKey*(cache: KeystoreCacheRef, keystore: Keystore, password: KeystorePass): Opt[seq[byte]] = if isNil(cache): return Opt.none(seq[byte]) let @@ -1132,7 +1132,7 @@ proc setCachedKey*(cache: KeystoreCacheRef, keystore: Keystore, let saltKey = keystore.getSaltKey(password) cache.table[saltKey] = KeystoreCacheItem.init(keystore, key) -proc destroyCacheKey*(cache: KeystoreCacheRef, +func destroyCacheKey*(cache: KeystoreCacheRef, keystore: Keystore, password: KeystorePass) = if isNil(cache): return let saltKey = keystore.getSaltKey(password) @@ -1206,7 +1206,7 @@ proc readValue*(reader: var JsonReader, value: var lcrypto.PublicKey) {. # TODO: Can we provide better diagnostic? raiseUnexpectedValue(reader, "Valid hex-encoded public key expected") -proc decryptNetKeystore*(nkeystore: NetKeystore, +func decryptNetKeystore*(nkeystore: NetKeystore, password: KeystorePass): KsResult[lcrypto.PrivateKey] = var secret: seq[byte] defer: burnMem(secret) @@ -1221,7 +1221,7 @@ proc decryptNetKeystore*(nkeystore: NetKeystore, else: err $status -proc decryptNetKeystore*(nkeystore: JsonString, +func decryptNetKeystore*(nkeystore: JsonString, password: KeystorePass): KsResult[lcrypto.PrivateKey] = try: let keystore = parseNetKeystore(string nkeystore) @@ -1229,10 +1229,10 @@ proc decryptNetKeystore*(nkeystore: JsonString, except SerializationError as exc: return err(exc.formatMsg("")) -proc generateKeystoreSalt*(rng: var HmacDrbgContext): seq[byte] = +func generateKeystoreSalt*(rng: var HmacDrbgContext): seq[byte] = rng.generateBytes(keyLen) -proc createCryptoField(kdfKind: KdfKind, +func createCryptoField(kdfKind: KdfKind, rng: var HmacDrbgContext, secret: openArray[byte], password = KeystorePass.init "", @@ -1339,7 +1339,7 @@ proc createKeystore*(kdfKind: KdfKind, uuid: $uuid, version: 4) -proc createRemoteKeystore*(pubKey: ValidatorPubKey, remoteUri: HttpHostUri, +func createRemoteKeystore*(pubKey: ValidatorPubKey, remoteUri: HttpHostUri, version = 1'u64, description = "", remoteType = RemoteSignerType.Web3Signer, flags: set[RemoteKeystoreFlag] = {}): RemoteKeystore = @@ -1387,10 +1387,10 @@ func makeWithdrawalCredentials*(k: ValidatorPubKey): Eth2Digest = bytes # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.2/specs/phase0/deposit-contract.md#withdrawal-credentials -proc makeWithdrawalCredentials*(k: CookedPubKey): Eth2Digest = +func makeWithdrawalCredentials*(k: CookedPubKey): Eth2Digest = makeWithdrawalCredentials(k.toPubKey()) -proc prepareDeposit*(cfg: RuntimeConfig, +func prepareDeposit*(cfg: RuntimeConfig, withdrawalPubKey: CookedPubKey, signingKey: ValidatorPrivKey, signingPubKey: CookedPubKey, amount = MAX_EFFECTIVE_BALANCE.Gwei): DepositData = diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index 850df60e9..8025331c5 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -579,7 +579,7 @@ func process_execution_layer_withdrawal_request*( exit_queue_epoch = compute_exit_epoch_and_update_churn(cfg, state, to_withdraw, cache) withdrawable_epoch = - Epoch(exit_queue_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY) + exit_queue_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY # In theory can fail, but failing/early returning here is indistinguishable discard state.pending_partial_withdrawals.add(PendingPartialWithdrawal( diff --git a/beacon_chain/sync/sync_manager.nim b/beacon_chain/sync/sync_manager.nim index 1f13549eb..933e6c312 100644 --- a/beacon_chain/sync/sync_manager.nim +++ b/beacon_chain/sync/sync_manager.nim @@ -110,7 +110,7 @@ proc initQueue[A, B](man: SyncManager[A, B]) = # there is present check `needsBackfill(). firstSlot else: - Slot(firstSlot - 1'u64) + firstSlot - 1'u64 man.queue = SyncQueue.init(A, man.direction, startSlot, lastSlot, man.chunkSize, man.getSafeSlot, man.blockVerifier, 1, man.ident) diff --git a/beacon_chain/validator_client/common.nim b/beacon_chain/validator_client/common.nim index 40cf6a637..ba09cc272 100644 --- a/beacon_chain/validator_client/common.nim +++ b/beacon_chain/validator_client/common.nim @@ -1511,7 +1511,7 @@ proc `+`*(slot: Slot, epochs: Epoch): Slot = func finish_slot*(epoch: Epoch): Slot = ## Return the last slot of ``epoch``. - Slot((epoch + 1).start_slot() - 1) + (epoch + 1).start_slot() - 1 proc getGraffitiBytes*(vc: ValidatorClientRef, validator: AttachedValidator): GraffitiBytes = diff --git a/ncli/ncli_common.nim b/ncli/ncli_common.nim index cb543ed69..e4d9b5e0f 100644 --- a/ncli/ncli_common.nim +++ b/ncli/ncli_common.nim @@ -126,14 +126,13 @@ static: "15227487_86601706.echop"]: # Wrong extension doAssert not filename.matchFilenameAggregatedFiles -proc getUnaggregatedFilesEpochRange*( - dir: string -): tuple[firstEpoch, lastEpoch: Epoch] {.raises: [OSError, ValueError].} = +proc getUnaggregatedFilesEpochRange*(dir: string): + tuple[firstEpoch, lastEpoch: Epoch] {.raises: [OSError, ValueError].} = var smallestEpochFileName = '9'.repeat(epochInfoFileNameDigitsCount) & epochFileNameExtension var largestEpochFileName = '0'.repeat(epochInfoFileNameDigitsCount) & epochFileNameExtension - for (_, fn) in walkDir(dir.string, relative = true): + for (_, fn) in walkDir(dir, relative = true): if fn.matchFilenameUnaggregatedFiles: if fn < smallestEpochFileName: smallestEpochFileName = fn @@ -151,7 +150,7 @@ proc getUnaggregatedFilesLastEpoch*( proc getAggregatedFilesLastEpoch*( dir: string): Epoch {.raises: [OSError, ValueError].}= var largestEpochInFileName = 0'u - for (_, fn) in walkDir(dir.string, relative = true): + for (_, fn) in walkDir(dir, relative = true): if fn.matchFilenameAggregatedFiles: let fileLastEpoch = parseUInt( fn[epochInfoFileNameDigitsCount + 1 .. 2 * epochInfoFileNameDigitsCount]) diff --git a/ncli/ncli_testnet.nim b/ncli/ncli_testnet.nim index 8e3017e8b..b9e0bbb67 100644 --- a/ncli/ncli_testnet.nim +++ b/ncli/ncli_testnet.nim @@ -553,8 +553,8 @@ proc sendDeposits(deposits: seq[LaunchPadDeposit], var web3 = await initWeb3(web3Url, privateKey) let gasPrice = int(await web3.provider.eth_gasPrice()) * 2 - let depositContract = web3.contractSender(DepositContract, - Eth1Address depositContractAddress) + let depositContract = web3.contractSender( + DepositContract, depositContractAddress) for i in 4200 ..< deposits.len: let dp = deposits[i] as DepositData @@ -656,7 +656,7 @@ when isMainModule: error "Failed to read an Eth1 private key from standard input" if privateKey.len > 0: - conf.privateKey = privateKey.string + conf.privateKey = privateKey case conf.cmd of StartUpCommand.createTestnet: diff --git a/ncli/nim.cfg b/ncli/nim.cfg index b6eeb89c4..1e7f1e9bc 100644 --- a/ncli/nim.cfg +++ b/ncli/nim.cfg @@ -11,5 +11,4 @@ --styleCheck:usages --styleCheck:error ---hint[ConvFromXtoItselfNotNeeded]:off --hint[Processing]:off diff --git a/research/block_sim.nim b/research/block_sim.nim index 38444afd0..b7443f8d5 100644 --- a/research/block_sim.nim +++ b/research/block_sim.nim @@ -326,8 +326,7 @@ cli do(slots = SLOTS_PER_EPOCH * 7, contribution: contribution, selection_proof: aggregator.selectionProof) - validatorPrivKey = - MockPrivKeys[aggregator.validatorIdx.ValidatorIndex] + validatorPrivKey = MockPrivKeys[aggregator.validatorIdx] signedContributionAndProof = SignedContributionAndProof( message: contributionAndProof, diff --git a/research/nim.cfg b/research/nim.cfg index dc3a25bb0..839359063 100644 --- a/research/nim.cfg +++ b/research/nim.cfg @@ -11,5 +11,4 @@ --styleCheck:usages --styleCheck:error ---hint[ConvFromXtoItselfNotNeeded]:off --hint[Processing]:off diff --git a/tests/nim.cfg b/tests/nim.cfg index c1028331c..1048d7084 100644 --- a/tests/nim.cfg +++ b/tests/nim.cfg @@ -1,5 +1,5 @@ # beacon_chain -# Copyright (c) 2019-2023 Status Research & Development GmbH +# Copyright (c) 2019-2024 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). @@ -11,5 +11,4 @@ --styleCheck:usages --styleCheck:hint ---hint[ConvFromXtoItselfNotNeeded]:off --hint[Processing]:off diff --git a/tests/test_blockchain_dag.nim b/tests/test_blockchain_dag.nim index d662337c0..33485f365 100644 --- a/tests/test_blockchain_dag.nim +++ b/tests/test_blockchain_dag.nim @@ -1336,8 +1336,7 @@ suite "State history": res = process_slots(cfg, dag.headState, 5.Slot, cache, info, flags = {}) check res.isOk for i in 0.Slot .. 5.Slot: - check state.getBlockIdAtSlot(i) == - Opt.some BlockSlotId.init(gen, i.Slot) + check state.getBlockIdAtSlot(i) == Opt.some BlockSlotId.init(gen, i) check state.getBlockIdAtSlot(6.Slot).isNone # Fill 5 slots diff --git a/tests/test_sync_committee_pool.nim b/tests/test_sync_committee_pool.nim index 6bfdc7148..78d6d6da3 100644 --- a/tests/test_sync_committee_pool.nim +++ b/tests/test_sync_committee_pool.nim @@ -103,7 +103,7 @@ suite "Sync committee pool": privkey2 = MockPrivKeys[1.ValidatorIndex] bid1 = BlockId( - slot: Slot(cfg.BELLATRIX_FORK_EPOCH.start_slot - 1), + slot: cfg.BELLATRIX_FORK_EPOCH.start_slot - 1, root: eth2digest(@[1.byte])) sig1 = get_sync_committee_message_signature( diff --git a/tests/test_sync_manager.nim b/tests/test_sync_manager.nim index 98455fc09..65fa75cd0 100644 --- a/tests/test_sync_manager.nim +++ b/tests/test_sync_manager.nim @@ -369,7 +369,7 @@ suite "SyncManager test suite": aq = newAsyncQueue[BlockEntry]() chunkSize = 3'u64 numberOfChunks = 3'u64 - finishSlot = Slot(startSlot + numberOfChunks * chunkSize - 1'u64) + finishSlot = startSlot + numberOfChunks * chunkSize - 1'u64 queueSize = 1 var counter = @@ -734,7 +734,7 @@ suite "SyncManager test suite": startSlot = Slot(0) chunkSize = SLOTS_PER_EPOCH numberOfChunks = 4'u64 - finishSlot = Slot(startSlot + numberOfChunks * chunkSize - 1'u64) + finishSlot = startSlot + numberOfChunks * chunkSize - 1'u64 queueSize = 1 var counter = int(startSlot) @@ -855,7 +855,7 @@ suite "SyncManager test suite": startSlot = Slot(0) chunkSize = SLOTS_PER_EPOCH numberOfChunks = 1'u64 - finishSlot = Slot(startSlot + numberOfChunks * chunkSize - 1'u64) + finishSlot = startSlot + numberOfChunks * chunkSize - 1'u64 queueSize = 1 var counter = int(startSlot) @@ -902,7 +902,7 @@ suite "SyncManager test suite": startSlot = Slot(0) chunkSize = SLOTS_PER_EPOCH numberOfChunks = 4'u64 - finishSlot = Slot(startSlot + numberOfChunks * chunkSize - 1'u64) + finishSlot = startSlot + numberOfChunks * chunkSize - 1'u64 queueSize = 1 var From c7bf6fb5421a781ea389b4c6be4f3ddad4969789 Mon Sep 17 00:00:00 2001 From: tersec Date: Thu, 23 May 2024 21:51:09 +0000 Subject: [PATCH 7/8] rm debugRaiseAssert; clean up several debugComments (#6308) * rm debugRaiseAssert; clean up several debugComments * exception linting --- .../consensus_object_pools/spec_cache.nim | 3 +- beacon_chain/light_client.nim | 2 - beacon_chain/spec/datatypes/base.nim | 1 - beacon_chain/spec/state_transition_epoch.nim | 41 +++++++++++-------- .../test_fixture_state_transition_epoch.nim | 2 +- .../test_fixture_state_transition_epoch.nim | 2 +- .../test_fixture_state_transition_epoch.nim | 3 +- .../test_fixture_state_transition_epoch.nim | 2 +- .../test_fixture_state_transition_epoch.nim | 4 +- tests/test_signing_node.nim | 3 +- 10 files changed, 33 insertions(+), 30 deletions(-) diff --git a/beacon_chain/consensus_object_pools/spec_cache.nim b/beacon_chain/consensus_object_pools/spec_cache.nim index b808c214e..a1f114f76 100644 --- a/beacon_chain/consensus_object_pools/spec_cache.nim +++ b/beacon_chain/consensus_object_pools/spec_cache.nim @@ -16,8 +16,7 @@ import ../spec/datatypes/base, ./block_pools_types, blockchain_dag -debugComment "probably just need Electra shortLog here" -import ../spec/forks # prune this, it's for electra attestation logging +from ../spec/datatypes/electra import shortLog export base, extras, block_pools_types, results diff --git a/beacon_chain/light_client.nim b/beacon_chain/light_client.nim index 232fe5361..746c3cb9a 100644 --- a/beacon_chain/light_client.nim +++ b/beacon_chain/light_client.nim @@ -366,8 +366,6 @@ proc updateGossipStatus*( lightClient: LightClient, slot: Slot, dagIsBehind = default(Option[bool])) = template cfg(): auto = lightClient.cfg - debugComment "when LC on electra works, add cfg.ELECTRA_FORK_EPOCH" - let epoch = slot.epoch diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 77df3d373..5dc1708d4 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -986,4 +986,3 @@ func ofLen*[T, N](ListType: type List[T, N], n: int): ListType = raise newException(SszSizeMismatchError) template debugComment*(s: string) = discard -template debugRaiseAssert*(s: string) = discard diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index ff83afb0c..06373511d 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -1221,7 +1221,7 @@ func process_historical_summaries_update*( # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-process_pending_balance_deposits func process_pending_balance_deposits*( cfg: RuntimeConfig, state: var electra.BeaconState, - cache: var StateCache) = + cache: var StateCache): Result[void, cstring] = let available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(cfg, state, cache) @@ -1232,8 +1232,9 @@ func process_pending_balance_deposits*( for deposit in state.pending_balance_deposits: if processed_amount + deposit.amount > available_for_processing: break - debugComment "do this validatorindex check properly (it truncates)" - increase_balance(state, deposit.index.ValidatorIndex, deposit.amount) + let deposit_validator_index = ValidatorIndex.init(deposit.index).valueOr: + return err("process_pending_balance_deposits: deposit index out of range") + increase_balance(state, deposit_validator_index, deposit.amount) processed_amount += deposit.amount inc next_deposit_index @@ -1247,8 +1248,12 @@ func process_pending_balance_deposits*( state.deposit_balance_to_consume = available_for_processing - processed_amount + ok() + # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.0/specs/electra/beacon-chain.md#new-process_pending_consolidations -func process_pending_consolidations*(cfg: RuntimeConfig, state: var electra.BeaconState) = +func process_pending_consolidations*( + cfg: RuntimeConfig, state: var electra.BeaconState): + Result[void, cstring] = var next_pending_consolidation = 0 for pending_consolidation in state.pending_consolidations: let source_validator = @@ -1259,25 +1264,29 @@ func process_pending_consolidations*(cfg: RuntimeConfig, state: var electra.Beac if source_validator.withdrawable_epoch > get_current_epoch(state): break + let + source_validator_index = ValidatorIndex.init( + pending_consolidation.source_index).valueOr: + return err("process_pending_consolidations: source index out of range") + target_validator_index = ValidatorIndex.init( + pending_consolidation.target_index).valueOr: + return err("process_pending_consolidations: target index out of range") + # Churn any target excess active balance of target and raise its max - debugComment "truncating integer conversion" - switch_to_compounding_validator( - state, pending_consolidation.target_index.ValidatorIndex) + switch_to_compounding_validator(state, target_validator_index) # Move active balance to target. Excess balance is withdrawable. - debugComment "Truncating" - let active_balance = get_active_balance( - state, pending_consolidation.source_index.ValidatorIndex) - decrease_balance( - state, pending_consolidation.source_index.ValidatorIndex, active_balance) - increase_balance( - state, pending_consolidation.target_index.ValidatorIndex, active_balance) + let active_balance = get_active_balance(state, source_validator_index) + decrease_balance(state, source_validator_index, active_balance) + increase_balance(state, target_validator_index, active_balance) inc next_pending_consolidation state.pending_consolidations = HashList[PendingConsolidation, Limit PENDING_CONSOLIDATIONS_LIMIT].init( state.pending_consolidations.asSeq[next_pending_consolidation..^1]) + ok() + # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#epoch-processing proc process_epoch*( cfg: RuntimeConfig, state: var phase0.BeaconState, flags: UpdateFlags, @@ -1464,8 +1473,8 @@ proc process_epoch*( process_slashings(state, info.balances.current_epoch) process_eth1_data_reset(state) - process_pending_balance_deposits(cfg, state, cache) # [New in Electra:EIP7251] - process_pending_consolidations(cfg, state) # [New in Electra:EIP7251] + ? process_pending_balance_deposits(cfg, state, cache) # [New in Electra:EIP7251] + ? process_pending_consolidations(cfg, state) # [New in Electra:EIP7251] process_effective_balance_updates(state) # [Modified in Electra:EIP7251] process_slashings_reset(state) process_randao_mixes_reset(state) diff --git a/tests/consensus_spec/altair/test_fixture_state_transition_epoch.nim b/tests/consensus_spec/altair/test_fixture_state_transition_epoch.nim index 7ad7e7149..ea7e3e827 100644 --- a/tests/consensus_spec/altair/test_fixture_state_transition_epoch.nim +++ b/tests/consensus_spec/altair/test_fixture_state_transition_epoch.nim @@ -11,7 +11,7 @@ import # Beacon chain internals chronicles, - ../../../beacon_chain/spec/[beaconstate, presets, state_transition_epoch], + ../../../beacon_chain/spec/[presets, state_transition_epoch], ../../../beacon_chain/spec/datatypes/altair, # Test utilities ../../testutil, diff --git a/tests/consensus_spec/bellatrix/test_fixture_state_transition_epoch.nim b/tests/consensus_spec/bellatrix/test_fixture_state_transition_epoch.nim index efca71f03..1c5b29acb 100644 --- a/tests/consensus_spec/bellatrix/test_fixture_state_transition_epoch.nim +++ b/tests/consensus_spec/bellatrix/test_fixture_state_transition_epoch.nim @@ -10,7 +10,7 @@ import # Beacon chain internals - ../../../beacon_chain/spec/[beaconstate, presets, state_transition_epoch], + ../../../beacon_chain/spec/[presets, state_transition_epoch], ../../../beacon_chain/spec/datatypes/[altair, bellatrix], # Test utilities ../../testutil, diff --git a/tests/consensus_spec/capella/test_fixture_state_transition_epoch.nim b/tests/consensus_spec/capella/test_fixture_state_transition_epoch.nim index 06de55857..96c35b7e1 100644 --- a/tests/consensus_spec/capella/test_fixture_state_transition_epoch.nim +++ b/tests/consensus_spec/capella/test_fixture_state_transition_epoch.nim @@ -5,13 +5,14 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. +{.push raises: [].} {.used.} import # Status internals chronicles, # Beacon chain internals - ../../../beacon_chain/spec/[beaconstate, presets, state_transition_epoch], + ../../../beacon_chain/spec/[presets, state_transition_epoch], ../../../beacon_chain/spec/datatypes/[altair, capella], # Test utilities ../../testutil, diff --git a/tests/consensus_spec/deneb/test_fixture_state_transition_epoch.nim b/tests/consensus_spec/deneb/test_fixture_state_transition_epoch.nim index 0f20db120..608880777 100644 --- a/tests/consensus_spec/deneb/test_fixture_state_transition_epoch.nim +++ b/tests/consensus_spec/deneb/test_fixture_state_transition_epoch.nim @@ -12,7 +12,7 @@ import # Status internals chronicles, # Beacon chain internals - ../../../beacon_chain/spec/[beaconstate, presets, state_transition_epoch], + ../../../beacon_chain/spec/[presets, state_transition_epoch], ../../../beacon_chain/spec/datatypes/[altair, deneb], # Test utilities ../../testutil, diff --git a/tests/consensus_spec/electra/test_fixture_state_transition_epoch.nim b/tests/consensus_spec/electra/test_fixture_state_transition_epoch.nim index e9bd6bc15..2c0c2c397 100644 --- a/tests/consensus_spec/electra/test_fixture_state_transition_epoch.nim +++ b/tests/consensus_spec/electra/test_fixture_state_transition_epoch.nim @@ -12,7 +12,7 @@ import # Status internals chronicles, # Beacon chain internals - ../../../beacon_chain/spec/[beaconstate, presets, state_transition_epoch], + ../../../beacon_chain/spec/[presets, state_transition_epoch], ../../../beacon_chain/spec/datatypes/[altair, electra], # Test utilities ../../testutil, @@ -146,13 +146,11 @@ runSuite(ParticipationFlagDir, "Participation flag updates"): # --------------------------------------------------------------- runSuite(PendingBalanceDepositsDir, "Pending balance deposits"): process_pending_balance_deposits(cfg, state, cache) - Result[void, cstring].ok() # Pending consolidations # --------------------------------------------------------------- runSuite(PendingConsolidationsDir, "Pending consolidations"): process_pending_consolidations(cfg, state) - Result[void, cstring].ok() # Sync committee updates # --------------------------------------------------------------- diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index 06453545c..f54f4e961 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -92,8 +92,7 @@ func init( of ConsensusFork.Deneb: return ForkedBeaconBlock.init(contents.denebData.signed_block.message) of ConsensusFork.Electra: - debugComment "probably like the deneb case" - return default(T) + return ForkedBeaconBlock.init(contents.electraData.signed_block.message) proc getBlock( fork: ConsensusFork, From 22efdd85a5acfc331629ff721957a5890392c527 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Fri, 24 May 2024 17:14:29 +0200 Subject: [PATCH 8/8] add tests for EIP-7495: SSZ StableContainer Implement test runner for new consensus-spec tests: - https://github.com/ethereum/consensus-specs/pull/3777 --- .gitmodules | 2 +- AllTests-mainnet.md | 30 +-- .../test_fixture_ssz_generic_types.nim | 226 ++++++++++++++++-- vendor/nim-ssz-serialization | 2 +- 4 files changed, 227 insertions(+), 33 deletions(-) diff --git a/.gitmodules b/.gitmodules index a42ebf3cc..dbbb68fcc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -184,7 +184,7 @@ path = vendor/nim-ssz-serialization url = https://github.com/status-im/nim-ssz-serialization.git ignore = untracked - branch = master + branch = feat/eip-7495 [submodule "vendor/nim-websock"] path = vendor/nim-websock url = https://github.com/status-im/nim-websock.git diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index ebdb530e5..4747b7f2e 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -431,20 +431,22 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 253/253 Fail: 0/253 Skip: 0/253 ## EF - SSZ generic types ```diff - Testing basic_vector inputs - invalid Skip -+ Testing basic_vector inputs - valid OK -+ Testing bitlist inputs - invalid OK -+ Testing bitlist inputs - valid OK - Testing bitvector inputs - invalid Skip -+ Testing bitvector inputs - valid OK -+ Testing boolean inputs - invalid OK -+ Testing boolean inputs - valid OK -+ Testing containers inputs - invalid - skipping BitsStruct OK -+ Testing containers inputs - valid - skipping BitsStruct OK -+ Testing uints inputs - invalid OK -+ Testing uints inputs - valid OK + Testing basic_vector inputs - invalid Skip ++ Testing basic_vector inputs - valid OK ++ Testing bitlist inputs - invalid OK ++ Testing bitlist inputs - valid OK + Testing bitvector inputs - invalid Skip ++ Testing bitvector inputs - valid OK ++ Testing boolean inputs - invalid OK ++ Testing boolean inputs - valid OK ++ Testing containers inputs - invalid - skipping BitsStruct OK ++ Testing containers inputs - valid - skipping BitsStruct OK ++ Testing profiles inputs - valid OK ++ Testing stablecontainers inputs - valid OK ++ Testing uints inputs - invalid OK ++ Testing uints inputs - valid OK ``` -OK: 10/12 Fail: 0/12 Skip: 2/12 +OK: 12/14 Fail: 0/14 Skip: 2/14 ## EIP-4881 ```diff + deposit_cases OK @@ -1030,4 +1032,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 687/692 Fail: 0/692 Skip: 5/692 +OK: 689/694 Fail: 0/694 Skip: 5/694 diff --git a/tests/consensus_spec/test_fixture_ssz_generic_types.nim b/tests/consensus_spec/test_fixture_ssz_generic_types.nim index 57c47fa85..2aa9e10d9 100644 --- a/tests/consensus_spec/test_fixture_ssz_generic_types.nim +++ b/tests/consensus_spec/test_fixture_ssz_generic_types.nim @@ -80,6 +80,132 @@ type D: BitList[6] E: BitArray[8] + # https://github.com/wemeetagain/consensus-specs/blob/eip-7495/tests/generators/ssz_generic/ssz_stablecontainer.py + SingleFieldTestStableStruct {.sszStableContainer: 4.} = object + A: Opt[byte] + + SmallTestStableStruct {.sszStableContainer: 4.} = object + A: Opt[uint16] + B: Opt[uint16] + + FixedTestStableStruct {.sszStableContainer: 4.} = object + A: Opt[uint8] + B: Opt[uint64] + C: Opt[uint32] + + VarTestStableStruct {.sszStableContainer: 4.} = object + A: Opt[uint16] + B: Opt[List[uint16, 1024]] + C: Opt[uint8] + + ComplexTestStableStruct {.sszStableContainer: 8.} = object + A: Opt[uint16] + B: Opt[List[uint16, 128]] + C: Opt[uint8] + D: Opt[List[byte, 256]] + E: Opt[VarTestStableStruct] + F: Opt[array[4, FixedTestStableStruct]] + G: Opt[array[2, VarTestStableStruct]] + + BitsStableStruct {.sszStableContainer: 8.} = object + A: Opt[BitList[5]] + B: Opt[BitArray[2]] + C: Opt[BitArray[1]] + D: Opt[BitList[6]] + E: Opt[BitArray[8]] + + # https://github.com/wemeetagain/consensus-specs/blob/eip-7495/tests/generators/ssz_generic/ssz_profile.py + SingleFieldTestProfile {.sszProfile: SingleFieldTestStableStruct.} = object + A: byte + + SmallTestProfile1 {.sszProfile: SmallTestStableStruct.} = object + A: uint16 + B: uint16 + + SmallTestProfile2 {.sszProfile: SmallTestStableStruct.} = object + A: uint16 + + SmallTestProfile3 {.sszProfile: SmallTestStableStruct.} = object + B: uint16 + + FixedTestProfile1 {.sszProfile: FixedTestStableStruct.} = object + A: uint8 + B: uint64 + C: uint32 + + FixedTestProfile2 {.sszProfile: FixedTestStableStruct.} = object + A: uint8 + B: uint64 + + FixedTestProfile3 {.sszProfile: FixedTestStableStruct.} = object + A: uint8 + C: uint32 + + FixedTestProfile4 {.sszProfile: FixedTestStableStruct.} = object + C: uint32 + + VarTestProfile1 {.sszProfile: VarTestStableStruct.} = object + A: uint16 + B: List[uint16, 1024] + C: uint8 + + VarTestProfile2 {.sszProfile: VarTestStableStruct.} = object + B: List[uint16, 1024] + C: uint8 + + VarTestProfile3 {.sszProfile: VarTestStableStruct.} = object + B: List[uint16, 1024] + + ComplexTestProfile1 {.sszProfile: ComplexTestStableStruct.} = object + A: uint16 + B: List[uint16, 128] + C: uint8 + D: List[byte, 256] + E: VarTestStableStruct + F: array[4, FixedTestStableStruct] + G: array[2, VarTestStableStruct] + + ComplexTestProfile2 {.sszProfile: ComplexTestStableStruct.} = object + A: uint16 + B: List[uint16, 128] + C: uint8 + D: List[byte, 256] + E: VarTestStableStruct + + ComplexTestProfile3 {.sszProfile: ComplexTestStableStruct.} = object + A: uint16 + C: uint8 + E: VarTestStableStruct + G: array[2, VarTestStableStruct] + + ComplexTestProfile4 {.sszProfile: ComplexTestStableStruct.} = object + B: List[uint16, 128] + D: List[byte, 256] + F: array[4, FixedTestStableStruct] + + ComplexTestProfile5 {.sszProfile: ComplexTestStableStruct.} = object + E: VarTestStableStruct + F: array[4, FixedTestStableStruct] + G: array[2, VarTestStableStruct] + + BitsProfile1 {.sszProfile: BitsStableStruct.} = object + A: BitList[5] + B: BitArray[2] + C: BitArray[1] + D: BitList[6] + E: BitArray[8] + + BitsProfile2 {.sszProfile: BitsStableStruct.} = object + A: BitList[5] + B: BitArray[2] + C: BitArray[1] + D: BitList[6] + + BitsProfile3 {.sszProfile: BitsStableStruct.} = object + A: BitList[5] + D: BitList[6] + E: BitArray[8] + # Type specific checks # ------------------------------------------------------------------------ @@ -285,6 +411,71 @@ proc sszCheck( of "BitsStruct": checkBasic(BitsStruct, dir, expectedHash) else: raise newException(ValueError, "unknown container in test: " & sszSubType) + of "profiles": + var name: string + let wasMatched = scanf(sszSubType, "$+_", name) + doAssert wasMatched + case name + of "BitsProfile1": + checkBasic(BitsProfile1, dir, expectedHash) + of "BitsProfile2": + checkBasic(BitsProfile2, dir, expectedHash) + of "BitsProfile3": + checkBasic(BitsProfile3, dir, expectedHash) + of "ComplexTestProfile1": + checkBasic(ComplexTestProfile1, dir, expectedHash) + of "ComplexTestProfile2": + checkBasic(ComplexTestProfile2, dir, expectedHash) + of "ComplexTestProfile3": + checkBasic(ComplexTestProfile3, dir, expectedHash) + of "ComplexTestProfile4": + checkBasic(ComplexTestProfile4, dir, expectedHash) + of "ComplexTestProfile5": + checkBasic(ComplexTestProfile5, dir, expectedHash) + of "FixedTestProfile1": + checkBasic(FixedTestProfile1, dir, expectedHash) + of "FixedTestProfile2": + checkBasic(FixedTestProfile2, dir, expectedHash) + of "FixedTestProfile3": + checkBasic(FixedTestProfile3, dir, expectedHash) + of "FixedTestProfile4": + checkBasic(FixedTestProfile4, dir, expectedHash) + of "SingleFieldTestProfile": + checkBasic(SingleFieldTestProfile, dir, expectedHash) + of "SmallTestProfile1": + checkBasic(SmallTestProfile1, dir, expectedHash) + of "SmallTestProfile2": + checkBasic(SmallTestProfile2, dir, expectedHash) + of "SmallTestProfile3": + checkBasic(SmallTestProfile3, dir, expectedHash) + of "VarTestProfile1": + checkBasic(VarTestProfile1, dir, expectedHash) + of "VarTestProfile2": + checkBasic(VarTestProfile2, dir, expectedHash) + of "VarTestProfile3": + checkBasic(VarTestProfile3, dir, expectedHash) + else: + raise newException(ValueError, "unknown profile in test: " & sszSubType) + of "stablecontainers": + var name: string + let wasMatched = scanf(sszSubType, "$+_", name) + doAssert wasMatched + case name + of "BitsStableStruct": + checkBasic(BitsStableStruct, dir, expectedHash) + of "ComplexTestStableStruct": + checkBasic(ComplexTestStableStruct, dir, expectedHash) + of "FixedTestStableStruct": + checkBasic(FixedTestStableStruct, dir, expectedHash) + of "SingleFieldTestStableStruct": + checkBasic(SingleFieldTestStableStruct, dir, expectedHash) + of "SmallTestStableStruct": + checkBasic(SmallTestStableStruct, dir, expectedHash) + of "VarTestStableStruct": + checkBasic(VarTestStableStruct, dir, expectedHash) + else: + raise newException(ValueError, + "unknown stablecontainer in test: " & sszSubType) else: raise newException(ValueError, "unknown ssz type in test: " & sszType) @@ -306,26 +497,27 @@ suite "EF - SSZ generic types": of "containers": skipped = " - skipping BitsStruct" - test &"Testing {sszType:12} inputs - valid" & skipped: + test &"Testing {sszType:16} inputs - valid" & skipped: let path = SSZDir/sszType/"valid" for pathKind, sszSubType in walkDir( path, relative = true, checkDir = true): if pathKind != pcDir: continue sszCheck(path, sszType, sszSubType) - test &"Testing {sszType:12} inputs - invalid" & skipped: - let path = SSZDir/sszType/"invalid" - for pathKind, sszSubType in walkDir( - path, relative = true, checkDir = true): - if pathKind != pcDir: continue - try: - sszCheck(path, sszType, sszSubType) - except SszError, UnconsumedInput: - discard - except TestSizeError as err: - echo err.msg - skip() - except: - checkpoint getStackTrace(getCurrentException()) - checkpoint getCurrentExceptionMsg() - check false + template invalidPath: untyped = SSZDir/sszType/"invalid" + if os_ops.dirExists(invalidPath): + test &"Testing {sszType:16} inputs - invalid" & skipped: + for pathKind, sszSubType in walkDir( + invalidPath, relative = true, checkDir = true): + if pathKind != pcDir: continue + try: + sszCheck(invalidPath, sszType, sszSubType) + except SszError, UnconsumedInput: + discard + except TestSizeError as err: + echo err.msg + skip() + except: + checkpoint getStackTrace(getCurrentException()) + checkpoint getCurrentExceptionMsg() + check false diff --git a/vendor/nim-ssz-serialization b/vendor/nim-ssz-serialization index 248f2bdca..695d1c9db 160000 --- a/vendor/nim-ssz-serialization +++ b/vendor/nim-ssz-serialization @@ -1 +1 @@ -Subproject commit 248f2bdca2d65ff920920c72b764d0622d522596 +Subproject commit 695d1c9dbec4fa0f746a15df259a19fed49e075e