Fluffy state network fixes and improvements (#2576)

* Cleanup tests.

* Improve offer sort function in state bridge.

* Verify nibble prefix for leaf and extension nodes during lookup.
This commit is contained in:
web3-developer 2024-08-23 15:46:23 +08:00 committed by GitHub
parent dbabe7e0a7
commit 507a9e71df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 37 additions and 24 deletions

View File

@ -50,7 +50,16 @@ proc getNextNodeHash(
) )
# leaf or extension node # leaf or extension node
let (_, isLeaf, prefix) = decodePrefix(trieNodeRlp.listElem(0)) let
(_, isLeaf, prefix) = decodePrefix(trieNodeRlp.listElem(0))
unpackedPrefix = prefix.unpackNibbles()
if unpackedPrefix != nibbles[nibbleIdx ..< nibbleIdx + unpackedPrefix.len()]:
# The nibbles don't match so we stop the search and don't increment
# the nibble index to indicate how many nibbles were consumed
return Opt.none((Nibbles, NodeHash))
nibbleIdx += prefix.unpackNibbles().len()
if isLeaf: if isLeaf:
return Opt.none((Nibbles, NodeHash)) return Opt.none((Nibbles, NodeHash))
@ -59,7 +68,6 @@ proc getNextNodeHash(
if nextHashBytes.len() == 0: if nextHashBytes.len() == 0:
return Opt.none((Nibbles, NodeHash)) return Opt.none((Nibbles, NodeHash))
nibbleIdx += prefix.unpackNibbles().len()
Opt.some( Opt.some(
(nibbles[0 ..< nibbleIdx].packNibbles(), KeccakHash.fromBytes(nextHashBytes)) (nibbles[0 ..< nibbleIdx].packNibbles(), KeccakHash.fromBytes(nextHashBytes))
) )
@ -91,7 +99,10 @@ proc getAccountProof(
key = AccountTrieNodeKey.init(nextPath, nextNodeHash) key = AccountTrieNodeKey.init(nextPath, nextNodeHash)
Opt.some(proof) if nibblesIdx < nibbles.len():
Opt.none(TrieProof)
else:
Opt.some(proof)
proc getStorageProof( proc getStorageProof(
n: StateNetwork, storageRoot: KeccakHash, address: EthAddress, storageKey: UInt256 n: StateNetwork, storageRoot: KeccakHash, address: EthAddress, storageKey: UInt256
@ -119,7 +130,10 @@ proc getStorageProof(
key = ContractTrieNodeKey.init(addressHash, nextPath, nextNodeHash) key = ContractTrieNodeKey.init(addressHash, nextPath, nextNodeHash)
Opt.some(proof) if nibblesIdx < nibbles.len():
Opt.none(TrieProof)
else:
Opt.some(proof)
proc getAccount( proc getAccount(
n: StateNetwork, blockHash: BlockHash, address: EthAddress n: StateNetwork, blockHash: BlockHash, address: EthAddress

View File

@ -61,12 +61,12 @@ func rlpDecodeContractTrieNode*(contractTrieNode: TrieNode): Result[UInt256, str
err(e.msg) err(e.msg)
func toAccount*(accountProof: TrieProof): Result[Account, string] {.inline.} = func toAccount*(accountProof: TrieProof): Result[Account, string] {.inline.} =
doAssert(accountProof.len() > 1) doAssert(accountProof.len() > 0)
rlpDecodeAccountTrieNode(accountProof[^1]) rlpDecodeAccountTrieNode(accountProof[^1])
func toSlot*(storageProof: TrieProof): Result[UInt256, string] {.inline.} = func toSlot*(storageProof: TrieProof): Result[UInt256, string] {.inline.} =
doAssert(storageProof.len() > 1) doAssert(storageProof.len() > 0)
rlpDecodeContractTrieNode(storageProof[^1]) rlpDecodeContractTrieNode(storageProof[^1])

View File

@ -77,20 +77,21 @@ proc getGenesisAlloc*(filePath: string): GenesisAlloc =
proc toState*( proc toState*(
alloc: GenesisAlloc alloc: GenesisAlloc
): (HexaryTrie, Table[EthAddress, HexaryTrie]) {.raises: [RlpError].} = ): (HexaryTrie, TableRef[EthAddress, HexaryTrie]) {.raises: [RlpError].} =
var accountTrie = initHexaryTrie(newMemoryDB()) var accountTrie = initHexaryTrie(newMemoryDB())
var storageStates = Table[EthAddress, HexaryTrie]() let storageStates = TableRef[EthAddress, HexaryTrie]()
for address, genAccount in alloc: for address, genAccount in alloc:
var storageRoot = EMPTY_ROOT_HASH var
var codeHash = EMPTY_CODE_HASH storageRoot = EMPTY_ROOT_HASH
codeHash = EMPTY_CODE_HASH
if genAccount.code.len() > 0: if genAccount.code.len() > 0:
var storageTrie = initHexaryTrie(newMemoryDB()) var storageTrie = initHexaryTrie(newMemoryDB())
for slotKey, slotValue in genAccount.storage: for slotKey, slotValue in genAccount.storage:
let key = keccakHash(toBytesBE(slotKey)).data let key = keccakHash(toBytesBE(slotKey)).data
let value = rlp.encode(slotValue) storageTrie.put(key, rlp.encode(slotValue))
storageTrie.put(key, value)
storageStates[address] = storageTrie storageStates[address] = storageTrie
storageRoot = storageTrie.rootHash() storageRoot = storageTrie.rootHash()
codeHash = keccakHash(genAccount.code) codeHash = keccakHash(genAccount.code)
@ -101,9 +102,7 @@ proc toState*(
storageRoot: storageRoot, storageRoot: storageRoot,
codeHash: codeHash, codeHash: codeHash,
) )
let key = keccakHash(address).data accountTrie.put(keccakHash(address).data, rlp.encode(account))
let value = rlp.encode(account)
accountTrie.put(key, value)
(accountTrie, storageStates) (accountTrie, storageStates)

View File

@ -117,7 +117,6 @@ suite "State Endpoints - Genesis JSON Files":
let let
storageProof = storageState.generateStorageProof(slotKey) storageProof = storageState.generateStorageProof(slotKey)
leafNode = storageProof[^1] leafNode = storageProof[^1]
slotKeyHash = keccakHash(toBytesBE(slotKey)).data
path = removeLeafKeyEndNibbles( path = removeLeafKeyEndNibbles(
Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), leafNode Nibbles.init(keccakHash(toBytesBE(slotKey)).data, true), leafNode
) )

View File

@ -54,7 +54,6 @@ procSuite "State Endpoints":
leafData = testData leafData = testData
contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList
contentKey = ContentKey.decode(contentKeyBytes).get() contentKey = ContentKey.decode(contentKeyBytes).get()
contentId = toContentId(contentKeyBytes)
contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValueBytes = leafData.content_value_offer.hexToSeqByte()
contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get()
@ -163,7 +162,6 @@ procSuite "State Endpoints":
leafData = testData leafData = testData
contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList
contentKey = ContentKey.decode(contentKeyBytes).get() contentKey = ContentKey.decode(contentKeyBytes).get()
contentId = toContentId(contentKeyBytes)
contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValueBytes = leafData.content_value_offer.hexToSeqByte()
contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get() contentValue = AccountTrieNodeOffer.decode(contentValueBytes).get()
@ -192,7 +190,6 @@ procSuite "State Endpoints":
leafData = testData leafData = testData
contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList contentKeyBytes = leafData.content_key.hexToSeqByte().ContentKeyByteList
contentKey = ContentKey.decode(contentKeyBytes).get() contentKey = ContentKey.decode(contentKeyBytes).get()
contentId = toContentId(contentKeyBytes)
contentValueBytes = leafData.content_value_offer.hexToSeqByte() contentValueBytes = leafData.content_value_offer.hexToSeqByte()
contentValue = ContractTrieNodeOffer.decode(contentValueBytes).get() contentValue = ContractTrieNodeOffer.decode(contentValueBytes).get()

View File

@ -59,7 +59,6 @@ procSuite "State Gossip - Gossip Offer":
parentContentKey = ContentKey.decode(parentContentKeyBytes).get() parentContentKey = ContentKey.decode(parentContentKeyBytes).get()
parentContentId = toContentId(parentContentKeyBytes) parentContentId = toContentId(parentContentKeyBytes)
parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte() parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte()
parentContentValue = AccountTrieNodeOffer.decode(parentContentValueBytes).get()
# set valid state root # set valid state root
stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot)
@ -133,7 +132,6 @@ procSuite "State Gossip - Gossip Offer":
parentContentKey = ContentKey.decode(parentContentKeyBytes).get() parentContentKey = ContentKey.decode(parentContentKeyBytes).get()
parentContentId = toContentId(parentContentKeyBytes) parentContentId = toContentId(parentContentKeyBytes)
parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte() parentContentValueBytes = parentTestData.content_value_offer.hexToSeqByte()
parentContentValue = ContractTrieNodeOffer.decode(parentContentValueBytes).get()
# set valid state root # set valid state root
stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot) stateNode1.mockBlockHashToStateRoot(contentValue.blockHash, stateRoot)

View File

@ -19,7 +19,7 @@ import
template checkValidProofsForExistingLeafs( template checkValidProofsForExistingLeafs(
genAccounts: GenesisAlloc, genAccounts: GenesisAlloc,
accountState: HexaryTrie, accountState: HexaryTrie,
storageStates: Table[EthAddress, HexaryTrie], storageStates: TableRef[EthAddress, HexaryTrie],
) = ) =
for address, account in genAccounts: for address, account in genAccounts:
var acc = newAccount(account.nonce, account.balance) var acc = newAccount(account.nonce, account.balance)
@ -74,7 +74,7 @@ template checkValidProofsForExistingLeafs(
template checkInvalidProofsWithBadValue( template checkInvalidProofsWithBadValue(
genAccounts: GenesisAlloc, genAccounts: GenesisAlloc,
accountState: HexaryTrie, accountState: HexaryTrie,
storageStates: Table[EthAddress, HexaryTrie], storageStates: TableRef[EthAddress, HexaryTrie],
) = ) =
for address, account in genAccounts: for address, account in genAccounts:
var acc = newAccount(account.nonce, account.balance) var acc = newAccount(account.nonce, account.balance)

View File

@ -282,7 +282,13 @@ proc runBackfillGossipBlockOffersLoop(
yId = ContentKeyByteList.init(y[0]).toContentId() yId = ContentKeyByteList.init(y[0]).toContentId()
xDistance = portalNodeId xor xId xDistance = portalNodeId xor xId
yDistance = portalNodeId xor yId yDistance = portalNodeId xor yId
if xDistance >= yDistance: 1 else: -1
if xDistance == yDistance:
0
elif xDistance > yDistance:
1
else:
-1
# Sort the offers based on the distance from the node so that we will gossip # Sort the offers based on the distance from the node so that we will gossip
# content that is closest to the node first # content that is closest to the node first