fix shanghai withdrawal validation

previously, the withdrawal validation is in process_block only,
but the one in persist block, which is also used in synchronizer
is not validated properly.
This commit is contained in:
jangko 2023-06-25 20:30:34 +07:00
parent f8c1a7f0a8
commit ff1a45e095
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
12 changed files with 58 additions and 67 deletions

View File

@ -226,7 +226,7 @@ jobs:
echo '```' >> release_notes.md echo '```' >> release_notes.md
- name: Delete tag - name: Delete tag
uses: dev-drprasad/delete-tag-and-release@v0.2.0 uses: dev-drprasad/delete-tag-and-release@v1.0.1
with: with:
delete_release: true delete_release: true
tag_name: nightly tag_name: nightly

View File

@ -162,7 +162,7 @@ jobs:
cat windows_amd64_stat/* >> stat_notes.md cat windows_amd64_stat/* >> stat_notes.md
- name: Delete tag - name: Delete tag
uses: dev-drprasad/delete-tag-and-release@v0.2.0 uses: dev-drprasad/delete-tag-and-release@v1.0.1
with: with:
delete_release: true delete_release: true
tag_name: sim-stat tag_name: sim-stat

View File

@ -9,7 +9,7 @@
# according to those terms. # according to those terms.
import import
std/[json, strutils, options], std/[json],
stew/byteutils, stew/byteutils,
../../../tools/common/helpers, ../../../tools/common/helpers,
../../../nimbus/common/chain_config ../../../nimbus/common/chain_config

View File

@ -94,6 +94,9 @@ proc toGenesisHeader*(
if g.difficulty.isZero and fork <= London: if g.difficulty.isZero and fork <= London:
result.difficulty = GENESIS_DIFFICULTY result.difficulty = GENESIS_DIFFICULTY
if fork >= Shanghai:
result.withdrawalsRoot = some(EMPTY_ROOT_HASH)
proc toGenesisHeader*( proc toGenesisHeader*(
genesis: Genesis; genesis: Genesis;
fork: HardFork; fork: HardFork;

View File

@ -14,8 +14,7 @@ import
../../common/common, ../../common/common,
../../utils/utils, ../../utils/utils,
../pow, ../pow,
../clique, ../clique
../validate
export export
common common

View File

@ -109,8 +109,7 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader];
let res = c.com.validateHeaderAndKinship( let res = c.com.validateHeaderAndKinship(
header, header,
body, body,
checkSealOK = false, # TODO: how to checkseal from here checkSealOK = false) # TODO: how to checkseal from here
pow = c.pow)
if res.isErr: if res.isErr:
debug "block validation error", debug "block validation error",
msg = res.error msg = res.error

View File

@ -85,20 +85,15 @@ proc procBlkPreamble(vmState: BaseVMState;
if vmState.determineFork >= FkShanghai: if vmState.determineFork >= FkShanghai:
if header.withdrawalsRoot.isNone: if header.withdrawalsRoot.isNone:
raise ValidationError.newException("Post-Shanghai block header must have withdrawalsRoot") raise ValidationError.newException("Post-Shanghai block header must have withdrawalsRoot")
elif body.withdrawals.isNone: if body.withdrawals.isNone:
raise ValidationError.newException("Post-Shanghai block body must have withdrawals") raise ValidationError.newException("Post-Shanghai block body must have withdrawals")
else:
if body.withdrawals.get.calcWithdrawalsRoot != header.withdrawalsRoot.get:
debug "Mismatched withdrawalsRoot",
blockNumber = header.blockNumber
return false
for withdrawal in body.withdrawals.get: for withdrawal in body.withdrawals.get:
vmState.stateDB.addBalance(withdrawal.address, withdrawal.amount.gwei) vmState.stateDB.addBalance(withdrawal.address, withdrawal.amount.gwei)
else: else:
if header.withdrawalsRoot.isSome: if header.withdrawalsRoot.isSome:
raise ValidationError.newException("Pre-Shanghai block header must not have withdrawalsRoot") raise ValidationError.newException("Pre-Shanghai block header must not have withdrawalsRoot")
elif body.withdrawals.isSome: if body.withdrawals.isSome:
raise ValidationError.newException("Pre-Shanghai block body must not have withdrawals") raise ValidationError.newException("Pre-Shanghai block body must not have withdrawals")
if vmState.cumulativeGasUsed != header.gasUsed: if vmState.cumulativeGasUsed != header.gasUsed:

View File

@ -10,7 +10,6 @@
import import
std/[sequtils, sets, times, strutils], std/[sequtils, sets, times, strutils],
../common/common,
../db/accounts_cache, ../db/accounts_cache,
".."/[transaction, common/common], ".."/[transaction, common/common],
".."/[errors], ".."/[errors],
@ -24,8 +23,6 @@ from stew/byteutils
import nil import nil
export export
pow.PowRef,
pow.new,
results results
{.push raises: [].} {.push raises: [].}
@ -73,8 +70,7 @@ proc validateSeal(pow: PowRef; header: BlockHeader): Result[void,string] =
ok() ok()
proc validateHeader(com: CommonRef; header, parentHeader: BlockHeader; proc validateHeader(com: CommonRef; header, parentHeader: BlockHeader;
txs: openArray[Transaction]; checkSealOK: bool; body: BlockBody; checkSealOK: bool): Result[void,string] =
pow: PowRef): Result[void,string] =
template inDAOExtraRange(blockNumber: BlockNumber): bool = template inDAOExtraRange(blockNumber: BlockNumber): bool =
# EIP-799 # EIP-799
@ -88,7 +84,7 @@ proc validateHeader(com: CommonRef; header, parentHeader: BlockHeader;
if header.extraData.len > 32: if header.extraData.len > 32:
return err("BlockHeader.extraData larger than 32 bytes") return err("BlockHeader.extraData larger than 32 bytes")
if header.gasUsed == 0 and 0 < txs.len: if header.gasUsed == 0 and 0 < body.transactions.len:
return err("zero gasUsed but transactions present"); return err("zero gasUsed but transactions present");
if header.gasUsed < 0 or header.gasUsed > header.gasLimit: if header.gasUsed < 0 or header.gasUsed > header.gasLimit:
@ -123,10 +119,10 @@ proc validateHeader(com: CommonRef; header, parentHeader: BlockHeader;
return err("provided header difficulty is too low") return err("provided header difficulty is too low")
if checkSealOK: if checkSealOK:
return pow.validateSeal(header) return com.pow.validateSeal(header)
? com.validateWithdrawals(header) ? com.validateWithdrawals(header, body)
? com.validateEip4844Header(header, parentHeader, txs) ? com.validateEip4844Header(header, parentHeader, body.transactions)
ok() ok()
@ -148,8 +144,8 @@ func validateUncle(currBlock, uncle, uncleParent: BlockHeader):
proc validateUncles(com: CommonRef; header: BlockHeader; proc validateUncles(com: CommonRef; header: BlockHeader;
uncles: openArray[BlockHeader]; checkSealOK: bool; uncles: openArray[BlockHeader];
pow: PowRef): Result[void,string] = checkSealOK: bool): Result[void,string] =
let hasUncles = uncles.len > 0 let hasUncles = uncles.len > 0
let shouldHaveUncles = header.ommersHash != EMPTY_UNCLE_HASH let shouldHaveUncles = header.ommersHash != EMPTY_UNCLE_HASH
@ -213,7 +209,7 @@ proc validateUncles(com: CommonRef; header: BlockHeader;
# Now perform VM level validation of the uncle # Now perform VM level validation of the uncle
if checkSealOK: if checkSealOK:
result = pow.validateSeal(uncle) result = com.pow.validateSeal(uncle)
if result.isErr: if result.isErr:
return return
@ -380,10 +376,8 @@ proc validateTransaction*(
proc validateHeaderAndKinship*( proc validateHeaderAndKinship*(
com: CommonRef; com: CommonRef;
header: BlockHeader; header: BlockHeader;
uncles: openArray[BlockHeader]; body: BlockBody;
txs: openArray[Transaction]; checkSealOK: bool): Result[void, string] =
checkSealOK: bool;
pow: PowRef): Result[void, string] =
if header.isGenesis: if header.isGenesis:
if header.extraData.len > 32: if header.extraData.len > 32:
return err("BlockHeader.extraData larger than 32 bytes") return err("BlockHeader.extraData larger than 32 bytes")
@ -396,41 +390,22 @@ proc validateHeaderAndKinship*(
return err("Failed to load block header from DB") return err("Failed to load block header from DB")
result = com.validateHeader( result = com.validateHeader(
header, parent, txs, checkSealOK, pow) header, parent, body, checkSealOK)
if result.isErr: if result.isErr:
return return
if uncles.len > MAX_UNCLES: if body.uncles.len > MAX_UNCLES:
return err("Number of uncles exceed limit.") return err("Number of uncles exceed limit.")
if not chainDB.exists(header.stateRoot): if not chainDB.exists(header.stateRoot):
return err("`state_root` was not found in the db.") return err("`state_root` was not found in the db.")
if com.consensus != ConsensusType.POS: if com.consensus != ConsensusType.POS:
result = com.validateUncles(header, uncles, checkSealOK, pow) result = com.validateUncles(header, body.uncles, checkSealOK)
if result.isOk: if result.isOk:
result = com.validateGasLimitOrBaseFee(header, parent) result = com.validateGasLimitOrBaseFee(header, parent)
proc validateHeaderAndKinship*(
com: CommonRef;
header: BlockHeader;
body: BlockBody;
checkSealOK: bool;
pow: PowRef): Result[void, string] =
com.validateHeaderAndKinship(
header, body.uncles, body.transactions, checkSealOK, pow)
proc validateHeaderAndKinship*(
com: CommonRef;
ethBlock: EthBlock;
checkSealOK: bool;
pow: PowRef): Result[void,string] =
com.validateHeaderAndKinship(
ethBlock.header, ethBlock.uncles, ethBlock.txs,
checkSealOK, pow)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# End # End
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -15,9 +15,25 @@ import
{.push raises: [].} {.push raises: [].}
# https://eips.ethereum.org/EIPS/eip-4895 # https://eips.ethereum.org/EIPS/eip-4895
func validateWithdrawals*( proc validateWithdrawals*(
com: CommonRef, header: BlockHeader com: CommonRef, header: BlockHeader, body: BlockBody
): Result[void, string] = ): Result[void, string] =
if header.withdrawalsRoot.isSome:
return err("Withdrawals not yet implemented") if com.forkGTE(Shanghai):
if header.withdrawalsRoot.isNone:
return err("Post-Shanghai block header must have withdrawalsRoot")
elif body.withdrawals.isNone:
return err("Post-Shanghai block body must have withdrawals")
else:
try:
if body.withdrawals.get.calcWithdrawalsRoot != header.withdrawalsRoot.get:
return err("Mismatched withdrawalsRoot blockNumber =" & $header.blockNumber)
except RlpError as ex:
return err(ex.msg)
else:
if header.withdrawalsRoot.isSome:
return err("Pre-Shanghai block header must not have withdrawalsRoot")
elif body.withdrawals.isSome:
return err("Pre-Shanghai block body must not have withdrawals")
return ok() return ok()

View File

@ -9,7 +9,7 @@
# according to those terms. # according to those terms.
import import
std/[math, macros], std/[macros],
stew/results, stew/results,
"."/[types, blake2b_f, blscurve], "."/[types, blake2b_f, blscurve],
./interpreter/[gas_meter, gas_costs, utils/utils_numeric], ./interpreter/[gas_meter, gas_costs, utils/utils_numeric],

View File

@ -180,7 +180,7 @@ proc validateDifficulty(ctx: LegacySyncRef,
return false return false
proc validateHeader(ctx: LegacySyncRef, header: BlockHeader, proc validateHeader(ctx: LegacySyncRef, header: BlockHeader,
txs: openArray[Transaction], body: BlockBody,
height = none(BlockNumber)): bool height = none(BlockNumber)): bool
{.raises: [CatchableError].} = {.raises: [CatchableError].} =
if header.parentHash == GENESIS_PARENT_HASH: if header.parentHash == GENESIS_PARENT_HASH:
@ -237,13 +237,13 @@ proc validateHeader(ctx: LegacySyncRef, header: BlockHeader,
parentNumber=parentHeader.blockNumber parentNumber=parentHeader.blockNumber
return false return false
res = com.validateWithdrawals(header) res = com.validateWithdrawals(header, body)
if res.isErr: if res.isErr:
trace "validate withdrawals error", trace "validate withdrawals error",
msg=res.error msg=res.error
return false return false
res = com.validateEip4844Header(header, parentHeader, txs) res = com.validateEip4844Header(header, parentHeader, body.transactions)
if res.isErr: if res.isErr:
trace "validate eip4844 error", trace "validate eip4844 error",
msg=res.error msg=res.error
@ -1053,7 +1053,13 @@ proc handleNewBlock(ctx: LegacySyncRef,
number=blk.header.blockNumber number=blk.header.blockNumber
return return
if not ctx.validateHeader(blk.header, blk.txs): let body = BlockBody(
transactions: blk.txs,
uncles: blk.uncles,
withdrawals: blk.withdrawals
)
if not ctx.validateHeader(blk.header, body):
error "invalid header from peer", error "invalid header from peer",
peer, hash=short(blk.header.blockHash) peer, hash=short(blk.header.blockHash)
return return

View File

@ -49,8 +49,6 @@ type
network : string network : string
postStateHash: Hash256 postStateHash: Hash256
var pow = PowRef.new
proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = false, trace = false) proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = false, trace = false)
func normalizeNumber(n: JsonNode): JsonNode = func normalizeNumber(n: JsonNode): JsonNode =
@ -206,7 +204,7 @@ proc importBlock(tester: var Tester, com: CommonRef,
if validation: if validation:
let rc = com.validateHeaderAndKinship( let rc = com.validateHeaderAndKinship(
tb.header, tb.body, checkSeal, pow) tb.header, tb.body, checkSeal)
if rc.isErr: if rc.isErr:
raise newException( raise newException(
ValidationError, "validateHeaderAndKinship: " & rc.error) ValidationError, "validateHeaderAndKinship: " & rc.error)
@ -259,7 +257,7 @@ proc runTester(tester: var Tester, com: CommonRef, testStatusIMPL: var TestStatu
# manually validating # manually validating
let res = com.validateHeaderAndKinship( let res = com.validateHeaderAndKinship(
tb.header, tb.body, checkSeal, pow) tb.header, tb.body, checkSeal)
check res.isOk check res.isOk
when defined(noisy): when defined(noisy):
if res.isErr: if res.isErr: