Storage of block witnesses (#1986)
* Added procs to get and store block witness in db and add generate-witness cli flag. * Completed initial implementation of block witness storage. * Added test to verify witness is persisted to db after call to persistBlock. * Update getBlockWitness to return witness using Result type. * Make generate witness parameter hidden.
This commit is contained in:
parent
9e50af839f
commit
332d294e77
|
@ -200,6 +200,12 @@ type
|
||||||
defaultValueDesc: ""
|
defaultValueDesc: ""
|
||||||
name: "verify-from" }: Option[uint64]
|
name: "verify-from" }: Option[uint64]
|
||||||
|
|
||||||
|
generateWitness* {.
|
||||||
|
hidden
|
||||||
|
desc: "Enable experimental generation and storage of block witnesses"
|
||||||
|
defaultValue: false
|
||||||
|
name: "generate-witness" }: bool
|
||||||
|
|
||||||
evm* {.
|
evm* {.
|
||||||
desc: "Load alternative EVM from EVMC-compatible shared library" & sharedLibText
|
desc: "Load alternative EVM from EVMC-compatible shared library" & sharedLibText
|
||||||
defaultValue: ""
|
defaultValue: ""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018 Status Research & Development GmbH
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
@ -34,6 +34,10 @@ type
|
||||||
## Trigger extra validation, currently within `persistBlocks()`
|
## Trigger extra validation, currently within `persistBlocks()`
|
||||||
## function only.
|
## function only.
|
||||||
|
|
||||||
|
generateWitness: bool ##\
|
||||||
|
## Enable generation of block witness, currently within `persistBlocks()`
|
||||||
|
## function only.
|
||||||
|
|
||||||
verifyFrom: BlockNumber ##\
|
verifyFrom: BlockNumber ##\
|
||||||
## First block to when `extraValidation` will be applied (only
|
## First block to when `extraValidation` will be applied (only
|
||||||
## effective if `extraValidation` is true.)
|
## effective if `extraValidation` is true.)
|
||||||
|
@ -101,6 +105,10 @@ proc extraValidation*(c: ChainRef): bool =
|
||||||
## Getter
|
## Getter
|
||||||
c.extraValidation
|
c.extraValidation
|
||||||
|
|
||||||
|
proc generateWitness*(c: ChainRef): bool =
|
||||||
|
## Getter
|
||||||
|
c.generateWitness
|
||||||
|
|
||||||
proc verifyFrom*(c: ChainRef): BlockNumber =
|
proc verifyFrom*(c: ChainRef): BlockNumber =
|
||||||
## Getter
|
## Getter
|
||||||
c.verifyFrom
|
c.verifyFrom
|
||||||
|
@ -125,6 +133,11 @@ proc `extraValidation=`*(c: ChainRef; extraValidation: bool) =
|
||||||
## extra block chain validation.
|
## extra block chain validation.
|
||||||
c.extraValidation = extraValidation
|
c.extraValidation = extraValidation
|
||||||
|
|
||||||
|
proc `generateWitness=`*(c: ChainRef; generateWitness: bool) =
|
||||||
|
## Setter. If set `true`, the assignment value `generateWitness` enables
|
||||||
|
## block witness generation.
|
||||||
|
c.generateWitness = generateWitness
|
||||||
|
|
||||||
proc `verifyFrom=`*(c: ChainRef; verifyFrom: BlockNumber) =
|
proc `verifyFrom=`*(c: ChainRef; verifyFrom: BlockNumber) =
|
||||||
## Setter. The assignment value `verifyFrom` defines the first block where
|
## Setter. The assignment value `verifyFrom` defines the first block where
|
||||||
## validation should start if the `Clique` field `extraValidation` was set
|
## validation should start if the `Clique` field `extraValidation` was set
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018-2023 Status Research & Development GmbH
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
@ -100,8 +100,11 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader];
|
||||||
msg = res.error
|
msg = res.error
|
||||||
return ValidationResult.Error
|
return ValidationResult.Error
|
||||||
|
|
||||||
|
if c.generateWitness:
|
||||||
|
vmState.generateWitness = true
|
||||||
|
|
||||||
let
|
let
|
||||||
validationResult = if c.validateBlock:
|
validationResult = if c.validateBlock or c.generateWitness:
|
||||||
vmState.processBlock(header, body)
|
vmState.processBlock(header, body)
|
||||||
else:
|
else:
|
||||||
ValidationResult.OK
|
ValidationResult.OK
|
||||||
|
@ -132,6 +135,21 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader];
|
||||||
msg = $rc.error
|
msg = $rc.error
|
||||||
return ValidationResult.Error
|
return ValidationResult.Error
|
||||||
|
|
||||||
|
if c.generateWitness:
|
||||||
|
let dbTx = c.db.beginTransaction()
|
||||||
|
defer: dbTx.dispose()
|
||||||
|
|
||||||
|
let
|
||||||
|
mkeys = vmState.stateDB.makeMultiKeys()
|
||||||
|
# Reset state to what it was before executing the block of transactions
|
||||||
|
initialState = BaseVMState.new(header, c.com)
|
||||||
|
witness = initialState.buildWitness(mkeys)
|
||||||
|
|
||||||
|
dbTx.rollback()
|
||||||
|
|
||||||
|
c.db.setBlockWitness(header.blockHash(), witness)
|
||||||
|
|
||||||
|
|
||||||
if NoPersistHeader notin flags:
|
if NoPersistHeader notin flags:
|
||||||
discard c.db.persistHeaderToDb(
|
discard c.db.persistHeaderToDb(
|
||||||
header, c.com.consensus == ConsensusType.POS, c.com.startOfHistory)
|
header, c.com.consensus == ConsensusType.POS, c.com.startOfHistory)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2018-2023 Status Research & Development GmbH
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
@ -734,6 +734,12 @@ proc haveBlockAndState*(db: CoreDbRef, headerHash: Hash256): bool =
|
||||||
# see if stateRoot exists
|
# see if stateRoot exists
|
||||||
db.exists(header.stateRoot)
|
db.exists(header.stateRoot)
|
||||||
|
|
||||||
|
proc getBlockWitness*(db: CoreDbRef, blockHash: Hash256): seq[byte] {.gcsafe.} =
|
||||||
|
db.kvt.get(blockHashToBlockWitnessKey(blockHash).toOpenArray)
|
||||||
|
|
||||||
|
proc setBlockWitness*(db: CoreDbRef, blockHash: Hash256, witness: seq[byte]) =
|
||||||
|
db.kvt.put(blockHashToBlockWitnessKey(blockHash).toOpenArray, witness)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -924,6 +924,21 @@ proc haveBlockAndState*(db: CoreDbRef, headerHash: Hash256): bool =
|
||||||
# see if stateRoot exists
|
# see if stateRoot exists
|
||||||
db.exists(header.stateRoot)
|
db.exists(header.stateRoot)
|
||||||
|
|
||||||
|
proc getBlockWitness*(
|
||||||
|
db: CoreDbRef, blockHash: Hash256): Result[seq[byte], string] {.gcsafe.} =
|
||||||
|
let res = db.newKvt(Shared)
|
||||||
|
.get(blockHashToBlockWitnessKey(blockHash).toOpenArray)
|
||||||
|
if res.isErr():
|
||||||
|
err("Failed to get block witness from database: " & $res.error.error)
|
||||||
|
else:
|
||||||
|
ok(res.value())
|
||||||
|
|
||||||
|
proc setBlockWitness*(db: CoreDbRef, blockHash: Hash256, witness: seq[byte]) =
|
||||||
|
let witnessKey = blockHashToBlockWitnessKey(blockHash)
|
||||||
|
db.newKvt.put(witnessKey.toOpenArray, witness).isOkOr:
|
||||||
|
warn logTxt "setBlockWitness()", witnessKey, action="put()", error=($$error)
|
||||||
|
return
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
@ -31,6 +31,7 @@ type
|
||||||
snapSyncAccount
|
snapSyncAccount
|
||||||
snapSyncStorageSlot
|
snapSyncStorageSlot
|
||||||
snapSyncStateRoot
|
snapSyncStateRoot
|
||||||
|
blockHashToBlockWitness
|
||||||
|
|
||||||
DbKey* = object
|
DbKey* = object
|
||||||
# The first byte stores the key type. The rest are key-specific values
|
# The first byte stores the key type. The rest are key-specific values
|
||||||
|
@ -129,6 +130,11 @@ proc snapSyncStateRootKey*(h: openArray[byte]): DbKey {.inline.} =
|
||||||
result.data[1 .. 32] = h
|
result.data[1 .. 32] = h
|
||||||
result.dataEndPos = uint8 sizeof(h)
|
result.dataEndPos = uint8 sizeof(h)
|
||||||
|
|
||||||
|
proc blockHashToBlockWitnessKey*(h: Hash256): DbKey {.inline.} =
|
||||||
|
result.data[0] = byte ord(blockHashToBlockWitness)
|
||||||
|
result.data[1 .. 32] = h.data
|
||||||
|
result.dataEndPos = uint8 32
|
||||||
|
|
||||||
template toOpenArray*(k: DbKey): openArray[byte] =
|
template toOpenArray*(k: DbKey): openArray[byte] =
|
||||||
k.data.toOpenArray(0, int(k.dataEndPos))
|
k.data.toOpenArray(0, int(k.dataEndPos))
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ proc basicServices(nimbus: NimbusNode,
|
||||||
nimbus.chainRef.extraValidation = 0 < verifyFrom
|
nimbus.chainRef.extraValidation = 0 < verifyFrom
|
||||||
nimbus.chainRef.verifyFrom = verifyFrom
|
nimbus.chainRef.verifyFrom = verifyFrom
|
||||||
|
|
||||||
|
nimbus.chainRef.generateWitness = conf.generateWitness
|
||||||
nimbus.beaconEngine = BeaconEngineRef.new(nimbus.txPool, nimbus.chainRef)
|
nimbus.beaconEngine = BeaconEngineRef.new(nimbus.txPool, nimbus.chainRef)
|
||||||
|
|
||||||
proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) =
|
proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) =
|
||||||
|
|
|
@ -47,7 +47,7 @@ proc getBlockWitness*(
|
||||||
vmState.generateWitness = true # Enable saving witness data
|
vmState.generateWitness = true # Enable saving witness data
|
||||||
vmState.com.hardForkTransition(blockHeader)
|
vmState.com.hardForkTransition(blockHeader)
|
||||||
|
|
||||||
var dbTx = vmState.com.db.beginTransaction()
|
let dbTx = vmState.com.db.beginTransaction()
|
||||||
defer: dbTx.dispose()
|
defer: dbTx.dispose()
|
||||||
|
|
||||||
# Execute the block of transactions and collect the keys of the touched account state
|
# Execute the block of transactions and collect the keys of the touched account state
|
||||||
|
|
|
@ -54,4 +54,5 @@ cliBuilder:
|
||||||
./test_beacon/test_skeleton,
|
./test_beacon/test_skeleton,
|
||||||
./test_overflow,
|
./test_overflow,
|
||||||
./test_getproof_json,
|
./test_getproof_json,
|
||||||
./test_rpc_experimental_json
|
./test_rpc_experimental_json,
|
||||||
|
./test_persistblock_witness_json
|
||||||
|
|
|
@ -335,5 +335,21 @@ proc configurationMain*() =
|
||||||
check conf.dataDir.string == defaultDataDir()
|
check conf.dataDir.string == defaultDataDir()
|
||||||
check conf.keyStore.string == "banana"
|
check conf.keyStore.string == "banana"
|
||||||
|
|
||||||
|
test "generate-witness default":
|
||||||
|
let conf = makeTestConfig()
|
||||||
|
check conf.generateWitness == false
|
||||||
|
|
||||||
|
test "generate-witness enabled":
|
||||||
|
let conf = makeConfig(@["--generate-witness"])
|
||||||
|
check conf.generateWitness == true
|
||||||
|
|
||||||
|
test "generate-witness equals true":
|
||||||
|
let conf = makeConfig(@["--generate-witness=true"])
|
||||||
|
check conf.generateWitness == true
|
||||||
|
|
||||||
|
test "generate-witness equals false":
|
||||||
|
let conf = makeConfig(@["--generate-witness=false"])
|
||||||
|
check conf.generateWitness == false
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
configurationMain()
|
configurationMain()
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[json, os, tables, strutils],
|
||||||
|
unittest2,
|
||||||
|
stew/byteutils,
|
||||||
|
./test_helpers,
|
||||||
|
../nimbus/core/chain,
|
||||||
|
../nimbus/common/common,
|
||||||
|
../stateless/[witness_verification, witness_types]
|
||||||
|
|
||||||
|
# use tracerTestGen.nim to generate additional test data
|
||||||
|
proc testFixture(node: JsonNode, testStatusIMPL: var TestStatus) =
|
||||||
|
var
|
||||||
|
blockNumber = UInt256.fromHex(node["blockNumber"].getStr())
|
||||||
|
memoryDB = newCoreDbRef LegacyDbMemory
|
||||||
|
config = chainConfigForNetwork(MainNet)
|
||||||
|
com = CommonRef.new(memoryDB, config, pruneTrie = false)
|
||||||
|
state = node["state"]
|
||||||
|
|
||||||
|
for k, v in state:
|
||||||
|
let key = hexToSeqByte(k)
|
||||||
|
let value = hexToSeqByte(v.getStr())
|
||||||
|
memoryDB.kvt.put(key, value)
|
||||||
|
|
||||||
|
let
|
||||||
|
parentNumber = blockNumber - 1
|
||||||
|
parent = com.db.getBlockHeader(parentNumber)
|
||||||
|
header = com.db.getBlockHeader(blockNumber)
|
||||||
|
headerHash = header.blockHash
|
||||||
|
blockBody = com.db.getBlockBody(headerHash)
|
||||||
|
chain = newChain(com)
|
||||||
|
headers = @[header]
|
||||||
|
bodies = @[blockBody]
|
||||||
|
|
||||||
|
chain.generateWitness = true # Enable code to generate and store witness in the db
|
||||||
|
|
||||||
|
# it's ok if setHead fails here because of missing ancestors
|
||||||
|
discard com.db.setHead(parent, true)
|
||||||
|
let validationResult = chain.persistBlocks(headers, bodies)
|
||||||
|
check validationResult == ValidationResult.OK
|
||||||
|
|
||||||
|
let
|
||||||
|
blockHash = memoryDB.getBlockHash(blockNumber)
|
||||||
|
witness = memoryDB.getBlockWitness(blockHash).value()
|
||||||
|
verifyWitnessResult = verifyWitness(parent.stateRoot, witness, {wfNoFlag})
|
||||||
|
|
||||||
|
check verifyWitnessResult.isOk()
|
||||||
|
let witnessData = verifyWitnessResult.value()
|
||||||
|
|
||||||
|
if blockBody.transactions.len() > 0:
|
||||||
|
check:
|
||||||
|
witness.len() > 0
|
||||||
|
witnessData.len() > 0
|
||||||
|
|
||||||
|
proc persistBlockWitnessJsonMain*() =
|
||||||
|
suite "persist block json tests":
|
||||||
|
jsonTest("PersistBlockTests", testFixture)
|
||||||
|
#var testStatusIMPL: TestStatus
|
||||||
|
#let n = json.parseFile("tests" / "fixtures" / "PersistBlockTests" / "block420301.json")
|
||||||
|
#testFixture(n, testStatusIMPL)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
persistBlockWitnessJsonMain()
|
Loading…
Reference in New Issue