remove unnecessary codes from test_blockchain_json
except for genesis block, we are not parsing block header from json node anymore. we parse block headers from block RLP, it is the same thing.
This commit is contained in:
parent
d1348824f8
commit
beb274d98c
|
@ -26,58 +26,46 @@ type
|
||||||
|
|
||||||
VMConfig = array[2, tuple[blockNumber: int, fork: Fork]]
|
VMConfig = array[2, tuple[blockNumber: int, fork: Fork]]
|
||||||
|
|
||||||
PlainBlock = object
|
EthBlock = object
|
||||||
header: BlockHeader
|
header: BlockHeader
|
||||||
transactions: seq[Transaction]
|
transactions: seq[Transaction]
|
||||||
uncles: seq[BlockHeader]
|
uncles: seq[BlockHeader]
|
||||||
|
|
||||||
TesterBlock = object
|
TestBlock = object
|
||||||
blockHeader: Option[BlockHeader]
|
goodBlock: bool
|
||||||
transactions: seq[Transaction]
|
blockRLP: Blob
|
||||||
uncles: seq[BlockHeader]
|
|
||||||
blockNumber: Option[int]
|
|
||||||
chainName: Option[string]
|
|
||||||
chainNetwork: Option[Fork]
|
|
||||||
exceptions: seq[(string, string)]
|
|
||||||
headerRLP: Blob
|
|
||||||
|
|
||||||
Tester = object
|
Tester = object
|
||||||
lastBlockHash: Hash256
|
lastBlockHash: Hash256
|
||||||
genesisBlockHeader: BlockHeader
|
genesisHeader: BlockHeader
|
||||||
blocks: seq[TesterBlock]
|
blocks: seq[TestBlock]
|
||||||
sealEngine: Option[SealEngine]
|
sealEngine: Option[SealEngine]
|
||||||
vmConfig: VMConfig
|
vmConfig: VMConfig
|
||||||
good: bool
|
|
||||||
debugMode: bool
|
debugMode: bool
|
||||||
trace: bool
|
trace: bool
|
||||||
vmState: BaseVMState
|
vmState: BaseVMState
|
||||||
debugData: JsonNode
|
debugData: JsonNode
|
||||||
network: string
|
network: string
|
||||||
|
|
||||||
MiningHeader* = object
|
MiningHeader = object
|
||||||
parentHash*: Hash256
|
parentHash: Hash256
|
||||||
ommersHash*: Hash256
|
ommersHash: Hash256
|
||||||
coinbase*: EthAddress
|
coinbase: EthAddress
|
||||||
stateRoot*: Hash256
|
stateRoot: Hash256
|
||||||
txRoot*: Hash256
|
txRoot: Hash256
|
||||||
receiptRoot*: Hash256
|
receiptRoot: Hash256
|
||||||
bloom*: common.BloomFilter
|
bloom: common.BloomFilter
|
||||||
difficulty*: DifficultyInt
|
difficulty: DifficultyInt
|
||||||
blockNumber*: BlockNumber
|
blockNumber: BlockNumber
|
||||||
gasLimit*: GasInt
|
gasLimit: GasInt
|
||||||
gasUsed*: GasInt
|
gasUsed: GasInt
|
||||||
timestamp*: EthTime
|
timestamp: EthTime
|
||||||
extraData*: Blob
|
extraData: Blob
|
||||||
|
|
||||||
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 =
|
||||||
let str = n.getStr
|
let str = n.getStr
|
||||||
# paranoid checks
|
|
||||||
doAssert n.kind == Jstring
|
|
||||||
doAssert str[0] == '0' and str[1] == 'x'
|
|
||||||
# real normalization
|
|
||||||
# strip leading 0
|
|
||||||
if str == "0x":
|
if str == "0x":
|
||||||
result = newJString("0x0")
|
result = newJString("0x0")
|
||||||
elif str == "0x0":
|
elif str == "0x0":
|
||||||
|
@ -120,72 +108,18 @@ proc parseHeader(blockHeader: JsonNode, testStatusIMPL: var TestStatus): BlockHe
|
||||||
blockHeader.fromJson "hash", blockHash
|
blockHeader.fromJson "hash", blockHash
|
||||||
check blockHash == hash(result)
|
check blockHash == hash(result)
|
||||||
|
|
||||||
proc parseTx*(n: JsonNode): Transaction =
|
proc parseBlocks(blocks: JsonNode): seq[TestBlock] =
|
||||||
|
|
||||||
for k, v in n:
|
|
||||||
case k
|
|
||||||
of "nonce", "gasPrice", "gasLimit", "value":
|
|
||||||
n[k] = normalizeNumber(v)
|
|
||||||
of "to":
|
|
||||||
let str = v.getStr
|
|
||||||
if str.len > 2 and str[1] != 'x':
|
|
||||||
n[k] = newJString("0x" & str)
|
|
||||||
of "v", "r", "s":
|
|
||||||
n[k] = normalizeNumber(v)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
n.fromJson "nonce", result.accountNonce
|
|
||||||
n.fromJson "gasPrice", result.gasPrice
|
|
||||||
n.fromJson "gasLimit", result.gasLimit
|
|
||||||
result.isContractCreation = n["to"].getStr == ""
|
|
||||||
if not result.isContractCreation:
|
|
||||||
n.fromJson "to", result.to
|
|
||||||
n.fromJson "value", result.value
|
|
||||||
n.fromJson "data", result.payload
|
|
||||||
n.fromJson "v", result.V
|
|
||||||
n.fromJson "r", result.R
|
|
||||||
n.fromJson "s", result.S
|
|
||||||
|
|
||||||
proc parseBlocks(blocks: JsonNode, testStatusIMPL: var TestStatus): seq[TesterBlock] =
|
|
||||||
result = @[]
|
|
||||||
|
|
||||||
for fixture in blocks:
|
for fixture in blocks:
|
||||||
var t: TesterBlock
|
var t: TestBlock
|
||||||
for key, value in fixture:
|
for key, value in fixture:
|
||||||
case key
|
case key
|
||||||
of "blockHeader":
|
of "blockHeader":
|
||||||
t.blockHeader = some(parseHeader(fixture["blockHeader"], testStatusIMPL))
|
# header is absent in bad block
|
||||||
of "blocknumber":
|
t.goodBlock = true
|
||||||
let numberStr = value.getStr
|
|
||||||
if numberStr.len >= 2 and numberStr[1] == 'x':
|
|
||||||
fixture[key] = normalizeNumber(value)
|
|
||||||
var number: int
|
|
||||||
fixture.fromJson "blocknumber", number
|
|
||||||
t.blockNumber = some(number)
|
|
||||||
else:
|
|
||||||
t.blockNumber = some(parseInt(numberStr))
|
|
||||||
of "chainname":
|
|
||||||
t.chainName = some(value.getStr)
|
|
||||||
of "chainnetwork":
|
|
||||||
t.chainNetWork = some(parseEnum[Fork](value.getStr))
|
|
||||||
of "rlp":
|
of "rlp":
|
||||||
fixture.fromJson "rlp", t.headerRLP
|
fixture.fromJson "rlp", t.blockRLP
|
||||||
of "transactions":
|
|
||||||
for tx in value:
|
|
||||||
t.transactions.add parseTx(tx)
|
|
||||||
of "uncleHeaders":
|
|
||||||
t.uncles = @[]
|
|
||||||
for uncle in value:
|
|
||||||
t.uncles.add parseHeader(uncle, testStatusIMPL)
|
|
||||||
else:
|
else:
|
||||||
t.exceptions.add( (key, value.getStr) )
|
discard
|
||||||
|
|
||||||
if t.blockHeader.isSome:
|
|
||||||
let h = t.blockHeader.get()
|
|
||||||
check calcTxRoot(t.transactions) == h.txRoot
|
|
||||||
let enc = rlp.encode(t.uncles)
|
|
||||||
check keccakHash(enc) == h.ommersHash
|
|
||||||
|
|
||||||
result.add t
|
result.add t
|
||||||
|
|
||||||
|
@ -260,29 +194,26 @@ func vmConfigToFork(vmConfig: VMConfig, blockNumber: Uint256): Fork =
|
||||||
raise newException(ValueError, "unreachable code")
|
raise newException(ValueError, "unreachable code")
|
||||||
|
|
||||||
proc parseTester(fixture: JsonNode, testStatusIMPL: var TestStatus): Tester =
|
proc parseTester(fixture: JsonNode, testStatusIMPL: var TestStatus): Tester =
|
||||||
result.good = true
|
result.blocks = parseBlocks(fixture["blocks"])
|
||||||
|
|
||||||
fixture.fromJson "lastblockhash", result.lastBlockHash
|
fixture.fromJson "lastblockhash", result.lastBlockHash
|
||||||
result.genesisBlockHeader = parseHeader(fixture["genesisBlockHeader"], testStatusIMPL)
|
result.genesisHeader = parseHeader(fixture["genesisBlockHeader"], testStatusIMPL)
|
||||||
|
|
||||||
if "genesisRLP" in fixture:
|
if "genesisRLP" in fixture:
|
||||||
var genesisRLP: Blob
|
var genesisRLP: Blob
|
||||||
fixture.fromJson "genesisRLP", genesisRLP
|
fixture.fromJson "genesisRLP", genesisRLP
|
||||||
let genesisBlock = PlainBlock(header: result.genesisBlockHeader)
|
let genesisBlock = EthBlock(header: result.genesisHeader)
|
||||||
check genesisRLP == rlp.encode(genesisBlock)
|
check genesisRLP == rlp.encode(genesisBlock)
|
||||||
|
else:
|
||||||
|
var goodBlock = true
|
||||||
|
for h in result.blocks:
|
||||||
|
goodBlock = goodBlock and h.goodBlock
|
||||||
|
check goodBlock == false
|
||||||
|
|
||||||
if "sealEngine" in fixture:
|
if "sealEngine" in fixture:
|
||||||
result.sealEngine = some(parseEnum[SealEngine](fixture["sealEngine"].getStr))
|
result.sealEngine = some(parseEnum[SealEngine](fixture["sealEngine"].getStr))
|
||||||
result.network = fixture["network"].getStr
|
result.network = fixture["network"].getStr
|
||||||
|
|
||||||
try:
|
|
||||||
result.blocks = parseBlocks(fixture["blocks"], testStatusIMPL)
|
|
||||||
except ValueError:
|
|
||||||
result.good = false
|
|
||||||
|
|
||||||
# TODO: implement missing VM
|
|
||||||
#if result.network in ["HomesteadToDaoAt5"]:
|
|
||||||
#result.good = false
|
|
||||||
|
|
||||||
proc blockWitness(vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
proc blockWitness(vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
||||||
let rootHash = vmState.accountDb.rootHash
|
let rootHash = vmState.accountDb.rootHash
|
||||||
let witness = vmState.buildWitness()
|
let witness = vmState.buildWitness()
|
||||||
|
@ -301,7 +232,7 @@ proc blockWitness(vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
||||||
if root != rootHash:
|
if root != rootHash:
|
||||||
raise newException(ValidationError, "Invalid trie generated from block witness")
|
raise newException(ValidationError, "Invalid trie generated from block witness")
|
||||||
|
|
||||||
proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
proc assignBlockRewards(minedBlock: EthBlock, vmState: BaseVMState, fork: Fork, chainDB: BaseChainDB) =
|
||||||
let blockReward = blockRewards[fork]
|
let blockReward = blockRewards[fork]
|
||||||
var mainReward = blockReward
|
var mainReward = blockReward
|
||||||
if minedBlock.header.ommersHash != EMPTY_UNCLE_HASH:
|
if minedBlock.header.ommersHash != EMPTY_UNCLE_HASH:
|
||||||
|
@ -343,7 +274,7 @@ proc assignBlockRewards(minedBlock: PlainBlock, vmState: BaseVMState, fork: Fork
|
||||||
if vmState.generateWitness:
|
if vmState.generateWitness:
|
||||||
blockWitness(vmState, fork, chainDB)
|
blockWitness(vmState, fork, chainDB)
|
||||||
|
|
||||||
proc processBlock(chainDB: BaseChainDB, vmState: BaseVMState, minedBlock: PlainBlock, fork: Fork) =
|
proc processBlock(chainDB: BaseChainDB, vmState: BaseVMState, minedBlock: EthBlock, fork: Fork) =
|
||||||
var dbTx = chainDB.db.beginTransaction()
|
var dbTx = chainDB.db.beginTransaction()
|
||||||
defer: dbTx.dispose()
|
defer: dbTx.dispose()
|
||||||
|
|
||||||
|
@ -373,7 +304,7 @@ proc processBlock(chainDB: BaseChainDB, vmState: BaseVMState, minedBlock: PlainB
|
||||||
# while still benefits from trie pruning
|
# while still benefits from trie pruning
|
||||||
dbTx.commit(applyDeletes = false)
|
dbTx.commit(applyDeletes = false)
|
||||||
|
|
||||||
func validateBlockUnchanged(a, b: PlainBlock): bool =
|
func validateBlockUnchanged(a, b: EthBlock): bool =
|
||||||
result = rlp.encode(a) == rlp.encode(b)
|
result = rlp.encode(a) == rlp.encode(b)
|
||||||
|
|
||||||
type Hash512 = MDigest[512]
|
type Hash512 = MDigest[512]
|
||||||
|
@ -507,7 +438,7 @@ proc validateGasLimit(chainDB: BaseChainDB, header: BlockHeader) =
|
||||||
elif header.gasLimit > highBound:
|
elif header.gasLimit > highBound:
|
||||||
raise newException(ValidationError, "The gas limit is too high")
|
raise newException(ValidationError, "The gas limit is too high")
|
||||||
|
|
||||||
proc validateUncles(chainDB: BaseChainDB, currBlock: PlainBlock, checkSeal: bool) =
|
proc validateUncles(chainDB: BaseChainDB, currBlock: EthBlock, checkSeal: bool) =
|
||||||
let hasUncles = currBlock.uncles.len > 0
|
let hasUncles = currBlock.uncles.len > 0
|
||||||
let shouldHaveUncles = currBlock.header.ommersHash != EMPTY_UNCLE_HASH
|
let shouldHaveUncles = currBlock.header.ommersHash != EMPTY_UNCLE_HASH
|
||||||
|
|
||||||
|
@ -559,10 +490,10 @@ proc validateUncles(chainDB: BaseChainDB, currBlock: PlainBlock, checkSeal: bool
|
||||||
let uncleParent = chainDB.getBlockHeader(uncle.parentHash)
|
let uncleParent = chainDB.getBlockHeader(uncle.parentHash)
|
||||||
validateUncle(currBlock.header, uncle, uncleParent)
|
validateUncle(currBlock.header, uncle, uncleParent)
|
||||||
|
|
||||||
func isGenesis(currBlock: PlainBlock): bool =
|
func isGenesis(currBlock: EthBlock): bool =
|
||||||
result = currBlock.header.blockNumber == 0.u256 and currBlock.header.parentHash == GENESIS_PARENT_HASH
|
result = currBlock.header.blockNumber == 0.u256 and currBlock.header.parentHash == GENESIS_PARENT_HASH
|
||||||
|
|
||||||
proc validateBlock(chainDB: BaseChainDB, currBlock: PlainBlock, checkSeal: bool): bool =
|
proc validateBlock(chainDB: BaseChainDB, currBlock: EthBlock, checkSeal: bool): bool =
|
||||||
if currBlock.isGenesis:
|
if currBlock.isGenesis:
|
||||||
if currBlock.header.extraData.len > 32:
|
if currBlock.header.extraData.len > 32:
|
||||||
raise newException(ValidationError, "BlockHeader.extraData larger than 32 bytes")
|
raise newException(ValidationError, "BlockHeader.extraData larger than 32 bytes")
|
||||||
|
@ -583,7 +514,7 @@ proc validateBlock(chainDB: BaseChainDB, currBlock: PlainBlock, checkSeal: bool)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc importBlock(tester: var Tester, chainDB: BaseChainDB,
|
proc importBlock(tester: var Tester, chainDB: BaseChainDB,
|
||||||
preminedBlock: PlainBlock, fork: Fork, checkSeal, validation = true): PlainBlock =
|
preminedBlock: EthBlock, fork: Fork, checkSeal, validation = true): EthBlock =
|
||||||
|
|
||||||
let parentHeader = chainDB.getBlockHeader(preminedBlock.header.parentHash)
|
let parentHeader = chainDB.getBlockHeader(preminedBlock.header.parentHash)
|
||||||
let baseHeaderForImport = generateHeaderFromParentHeader(chainDB.config,
|
let baseHeaderForImport = generateHeaderFromParentHeader(chainDB.config,
|
||||||
|
@ -612,14 +543,14 @@ proc importBlock(tester: var Tester, chainDB: BaseChainDB,
|
||||||
|
|
||||||
discard chainDB.persistHeaderToDb(preminedBlock.header)
|
discard chainDB.persistHeaderToDb(preminedBlock.header)
|
||||||
|
|
||||||
proc applyFixtureBlockToChain(tester: var Tester, tb: TesterBlock,
|
proc applyFixtureBlockToChain(tester: var Tester, tb: TestBlock,
|
||||||
chainDB: BaseChainDB, checkSeal, validation = true): (PlainBlock, PlainBlock, Blob) =
|
chainDB: BaseChainDB, checkSeal, validation = true): (EthBlock, EthBlock, Blob) =
|
||||||
|
|
||||||
# we hack the ChainConfig here and let it works with calcDifficulty
|
# we hack the ChainConfig here and let it works with calcDifficulty
|
||||||
tester.vmConfig = vmConfiguration(tester.network, chainDB.config)
|
tester.vmConfig = vmConfiguration(tester.network, chainDB.config)
|
||||||
|
|
||||||
var
|
var
|
||||||
preminedBlock = rlp.decode(tb.headerRLP, PlainBlock)
|
preminedBlock = rlp.decode(tb.blockRLP, EthBlock)
|
||||||
fork = vmConfigToFork(tester.vmConfig, preminedBlock.header.blockNumber)
|
fork = vmConfigToFork(tester.vmConfig, preminedBlock.header.blockNumber)
|
||||||
minedBlock = tester.importBlock(chainDB, preminedBlock, fork, checkSeal, validation)
|
minedBlock = tester.importBlock(chainDB, preminedBlock, fork, checkSeal, validation)
|
||||||
rlpEncodedMinedBlock = rlp.encode(minedBlock)
|
rlpEncodedMinedBlock = rlp.encode(minedBlock)
|
||||||
|
@ -638,20 +569,18 @@ proc collectDebugData(tester: var Tester) =
|
||||||
}
|
}
|
||||||
|
|
||||||
proc runTester(tester: var Tester, chainDB: BaseChainDB, testStatusIMPL: var TestStatus) =
|
proc runTester(tester: var Tester, chainDB: BaseChainDB, testStatusIMPL: var TestStatus) =
|
||||||
discard chainDB.persistHeaderToDb(tester.genesisBlockHeader)
|
discard chainDB.persistHeaderToDb(tester.genesisHeader)
|
||||||
check chainDB.getCanonicalHead().blockHash == tester.genesisBlockHeader.blockHash
|
check chainDB.getCanonicalHead().blockHash == tester.genesisHeader.blockHash
|
||||||
let checkSeal = tester.shouldCheckSeal
|
let checkSeal = tester.shouldCheckSeal
|
||||||
|
|
||||||
if tester.debugMode:
|
if tester.debugMode:
|
||||||
tester.debugData = newJArray()
|
tester.debugData = newJArray()
|
||||||
|
|
||||||
for idx, testerBlock in tester.blocks:
|
for idx, TestBlock in tester.blocks:
|
||||||
let shouldBeGoodBlock = testerBlock.blockHeader.isSome
|
if TestBlock.goodBlock:
|
||||||
|
|
||||||
if shouldBeGoodBlock:
|
|
||||||
try:
|
try:
|
||||||
let (preminedBlock, _, _) = tester.applyFixtureBlockToChain(
|
let (preminedBlock, _, _) = tester.applyFixtureBlockToChain(
|
||||||
testerBlock, chainDB, checkSeal, validation = false) # we manually validate below
|
TestBlock, chainDB, checkSeal, validation = false) # we manually validate below
|
||||||
check validateBlock(chainDB, preminedBlock, checkSeal) == true
|
check validateBlock(chainDB, preminedBlock, checkSeal) == true
|
||||||
except:
|
except:
|
||||||
debugEcho "FATAL ERROR(WE HAVE BUG): ", getCurrentExceptionMsg()
|
debugEcho "FATAL ERROR(WE HAVE BUG): ", getCurrentExceptionMsg()
|
||||||
|
@ -659,7 +588,7 @@ proc runTester(tester: var Tester, chainDB: BaseChainDB, testStatusIMPL: var Tes
|
||||||
else:
|
else:
|
||||||
var noError = true
|
var noError = true
|
||||||
try:
|
try:
|
||||||
let (_, _, _) = tester.applyFixtureBlockToChain(testerBlock,
|
let (_, _, _) = tester.applyFixtureBlockToChain(TestBlock,
|
||||||
chainDB, checkSeal, validation = true)
|
chainDB, checkSeal, validation = true)
|
||||||
except ValueError, ValidationError, BlockNotFound, MalformedRlpError, RlpTypeMismatch:
|
except ValueError, ValidationError, BlockNotFound, MalformedRlpError, RlpTypeMismatch:
|
||||||
# failure is expected on this bad block
|
# failure is expected on this bad block
|
||||||
|
@ -718,10 +647,8 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
|
||||||
var tester = parseTester(fixture, testStatusIMPL)
|
var tester = parseTester(fixture, testStatusIMPL)
|
||||||
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = test_config.getConfiguration().pruning)
|
var chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = test_config.getConfiguration().pruning)
|
||||||
|
|
||||||
if not tester.good: continue
|
|
||||||
|
|
||||||
var vmState = newBaseVMState(emptyRlpHash,
|
var vmState = newBaseVMState(emptyRlpHash,
|
||||||
tester.genesisBlockHeader, chainDB)
|
tester.genesisHeader, chainDB)
|
||||||
|
|
||||||
vmState.generateWitness = true
|
vmState.generateWitness = true
|
||||||
|
|
||||||
|
@ -730,7 +657,7 @@ proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus, debugMode = fal
|
||||||
db.persist()
|
db.persist()
|
||||||
|
|
||||||
let obtainedHash = $(vmState.readOnlyStateDB.rootHash)
|
let obtainedHash = $(vmState.readOnlyStateDB.rootHash)
|
||||||
check obtainedHash == $(tester.genesisBlockHeader.stateRoot)
|
check obtainedHash == $(tester.genesisHeader.stateRoot)
|
||||||
|
|
||||||
tester.debugMode = debugMode
|
tester.debugMode = debugMode
|
||||||
tester.trace = trace
|
tester.trace = trace
|
||||||
|
@ -802,7 +729,7 @@ when isMainModule:
|
||||||
# genesisRLP -> NOT every fixture has it, rlp bytes of genesis block header
|
# genesisRLP -> NOT every fixture has it, rlp bytes of genesis block header
|
||||||
# _info -> every fixture has it, can be omitted
|
# _info -> every fixture has it, can be omitted
|
||||||
# pre, postState -> every fixture has it, prestate and post state
|
# pre, postState -> every fixture has it, prestate and post state
|
||||||
# genesisBlockHeader -> every fixture has it
|
# genesisHeader -> every fixture has it
|
||||||
# network -> every fixture has it
|
# network -> every fixture has it
|
||||||
# # EIP150 247
|
# # EIP150 247
|
||||||
# # ConstantinopleFix 286
|
# # ConstantinopleFix 286
|
||||||
|
|
Loading…
Reference in New Issue