Add accumulator buildProof, verifyProof and verifyHeader (#1132)

This commit is contained in:
Kim De Mey 2022-06-23 21:00:59 +02:00 committed by GitHub
parent bf511b091e
commit d4d4e8c28f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 1 deletions

View File

@ -217,3 +217,103 @@ proc buildAccumulator*(dataFile: string): Result[Accumulator, string] =
headers[header.blockNumber.truncate(int)] = header
ok(buildAccumulator(headers))
## Calls and helper calls for building header proofs and verifying headers
## against the Accumulator and the header proofs.
func inCurrentEpoch*(header: BlockHeader, a: Accumulator): bool =
let blockNumber = header.blockNumber.truncate(uint64)
blockNumber > uint64(a.historicalEpochs.len() * epochSize) - 1
func getEpochIndex(header: BlockHeader): uint64 =
## Get the index for the historical epochs
header.blockNumber.truncate(uint64) div epochSize
func getHeaderRecordIndex(header: BlockHeader, epochIndex: uint64): uint64 =
## Get the relative header index for the epoch accumulator
uint64(header.blockNumber.truncate(uint64) - epochIndex * epochSize)
proc buildProof*(db: AccumulatorDB, header: BlockHeader):
Result[seq[Digest], string] =
let accumulatorOpt = db.getAccumulator()
if accumulatorOpt.isNone():
return err("Master accumulator not found in database")
let
accumulator = accumulatorOpt.get()
epochIndex = getEpochIndex(header)
epochHash = Digest(data: accumulator.historicalEpochs[epochIndex])
key = ContentKey(
contentType: epochAccumulator,
epochAccumulatorKey: EpochAccumulatorKey(
epochHash: epochHash))
epochAccumulatorOpt = db.getEpochAccumulator(key.toContentId())
if epochAccumulatorOpt.isNone():
return err("Epoch accumulator not found in database")
let
epochAccumulator = epochAccumulatorOpt.get()
headerRecordIndex = getHeaderRecordIndex(header, epochIndex)
# TODO: Implement more generalized `get_generalized_index`
gIndex = GeneralizedIndex(epochSize*2*2 + (headerRecordIndex*2))
epochAccumulator.build_proof(gIndex)
func verifyProof*(
a: Accumulator, proof: openArray[Digest], header: BlockHeader): bool =
let
epochIndex = getEpochIndex(header)
epochAccumulatorHash = Digest(data: a.historicalEpochs[epochIndex])
leave = hash_tree_root(header.blockHash())
headerRecordIndex = getHeaderRecordIndex(header, epochIndex)
# TODO: Implement more generalized `get_generalized_index`
gIndex = GeneralizedIndex(epochSize*2*2 + (headerRecordIndex*2))
verify_merkle_multiproof(@[leave], proof, @[gIndex], epochAccumulatorHash)
proc verifyProof*(
db: AccumulatorDB, proof: openArray[Digest], header: BlockHeader):
Result[void, string] =
let accumulatorOpt = db.getAccumulator()
if accumulatorOpt.isNone():
return err("Master accumulator not found in database")
if accumulatorOpt.get().verifyProof(proof, header):
ok()
else:
err("Proof verification failed")
proc verifyHeader*(
db: AccumulatorDB, header: BlockHeader, proof: Option[seq[Digest]]):
Result[void, string] =
let accumulatorOpt = db.getAccumulator()
if accumulatorOpt.isNone():
return err("Master accumulator not found in database")
let accumulator = accumulatorOpt.get()
if header.inCurrentEpoch(accumulator):
let blockNumber = header.blockNumber.truncate(uint64)
let relIndex = blockNumber - uint64(accumulator.historicalEpochs.len()) * epochSize
if relIndex > uint64(accumulator.currentEpoch.len() - 1):
return err("Blocknumber ahead of accumulator")
if accumulator.currentEpoch[relIndex].blockHash == header.blockHash():
ok()
else:
err("Header not part of canonical chain")
else:
if proof.isSome():
if accumulator.verifyProof(proof.get, header):
ok()
else:
err("Proof verification failed")
else:
err("Need proof to verify header")

View File

@ -48,3 +48,61 @@ suite "Header Accumulator":
updateAccumulator(accumulator, headers[i])
check accumulator.hash_tree_root().data.toHex() == hashTreeRoots[i]
test "Header Accumulator Proofs":
const
# Amount of headers to be created and added to the accumulator
amount = 25000
# Headers to test verification for
headersToTest = [
0,
epochSize - 1,
epochSize,
epochSize*2 - 1,
epochSize*2,
epochSize*3 - 1,
epochSize*3,
epochSize*3 + 1,
amount - 1]
var headers: seq[BlockHeader]
for i in 0..<amount:
# Note: These test headers will not be a blockchain, as the parent hashes
# are not properly filled in. That's fine however for this test, as that
# is not the way the headers are verified with the accumulator.
headers.add(BlockHeader(
blockNumber: i.stuint(256), difficulty: 1.stuint(256)))
let db = AccumulatorDB.new("", inMemory = true)
db.buildAccumulator(headers)
let accumulatorOpt = db.getAccumulator()
check accumulatorOpt.isSome()
let accumulator = accumulatorOpt.get()
block: # Test valid headers
for i in headersToTest:
let header = headers[i]
let proofOpt =
if header.inCurrentEpoch(accumulator):
none(seq[Digest])
else:
let proof = db.buildProof(header)
check proof.isOk()
some(proof.get())
check db.verifyHeader(header, proofOpt).isOk()
block: # Test some invalid headers
# Test a header with block number > than latest in accumulator
let header = BlockHeader(blockNumber: 25000.stuint(256))
check db.verifyHeader(header, none(seq[Digest])).isErr()
# Test different block headers by altering the difficulty
for i in headersToTest:
let header = BlockHeader(
blockNumber: i.stuint(256), difficulty: 2.stuint(256))
check db.verifyHeader(header, none(seq[Digest])).isErr()

@ -1 +1 @@
Subproject commit cd500484e054ead951f2d07aeb81c1c8c695db26
Subproject commit da3c08c16da2e4d2e9f48556fbdbf90bfff22172