1
- import { ENR } from '@chainsafe/discv5'
1
+ import { ENR , distance } from '@chainsafe/discv5'
2
2
import { fromHexString , toHexString } from '@chainsafe/ssz'
3
- import { bytesToInt , hexToBytes } from '@ethereumjs/util'
3
+ import { BranchNode , ExtensionNode , Trie , decodeNode } from '@ethereumjs/trie'
4
+ import { bytesToHex , bytesToInt , hexToBytes } from '@ethereumjs/util'
4
5
import debug from 'debug'
5
6
6
7
import { shortId } from '../../util/util.js'
@@ -16,7 +17,13 @@ import { BaseNetwork } from '../network.js'
16
17
import { NetworkId } from '../types.js'
17
18
18
19
import { StateDB } from './statedb.js'
19
- import { StateNetworkContentType } from './types.js'
20
+ import { AccountTrieNodeOffer , AccountTrieNodeRetrieval , StateNetworkContentType } from './types.js'
21
+ import {
22
+ AccountTrieNodeContentKey ,
23
+ StateNetworkContentId ,
24
+ tightlyPackNibbles ,
25
+ unpackNibbles ,
26
+ } from './util.js'
20
27
21
28
import type { PortalNetwork } from '../../client/client.js'
22
29
import type { FindContentMessage } from '../../wire/types.js'
@@ -135,4 +142,52 @@ export class StateNetwork extends BaseNetwork {
135
142
this . logger ( `content added for: ${ contentKey } ` )
136
143
this . emit ( 'ContentAdded' , contentKey , contentType , content )
137
144
}
145
+
146
+ async receiveAccountTrieNodeOffer ( contentKey : Uint8Array , content : Uint8Array ) {
147
+ const { path } = AccountTrieNodeContentKey . decode ( contentKey )
148
+ const { blockHash, proof } = AccountTrieNodeOffer . deserialize ( content )
149
+
150
+ await this . gossipContent ( contentKey , content )
151
+
152
+ const stateRoot = new Trie ( { useKeyHashing : true } ) [ 'hash' ] ( proof [ 0 ] )
153
+ this . stateDB . storeBlock ( { blockHash, stateRoot } )
154
+
155
+ const nibbles = unpackNibbles ( path . packedNibbles , path . isOddLength )
156
+ const newpaths = [ ...nibbles ]
157
+ const nodes = [ ...proof ]
158
+ nodes . pop ( )
159
+ const gossipContents : { contentKey : Uint8Array ; content : Uint8Array } [ ] = [ ]
160
+
161
+ while ( nodes . length > 0 ) {
162
+ const rlp = nodes . pop ( ) !
163
+ const curNode = decodeNode ( rlp )
164
+ if ( curNode instanceof BranchNode ) {
165
+ newpaths . pop ( )
166
+ } else if ( curNode instanceof ExtensionNode ) {
167
+ newpaths . splice ( - curNode . key ( ) . length )
168
+ } else {
169
+ throw new Error ( 'Should have already removed leaf node from array' )
170
+ }
171
+ const nodeHash = new Trie ( { useKeyHashing : true } ) [ 'hash' ] ( rlp )
172
+ const contentKey = AccountTrieNodeContentKey . encode ( {
173
+ nodeHash,
174
+ path : tightlyPackNibbles ( newpaths ) ,
175
+ } )
176
+ const gossipContent = AccountTrieNodeOffer . serialize ( { blockHash, proof : nodes } )
177
+ gossipContents . push ( { contentKey, content : gossipContent } )
178
+ const contentId = StateNetworkContentId . fromBytes ( contentKey )
179
+ const in_radius = distance ( bytesToHex ( contentId ) , this . enr . nodeId ) < this . nodeRadius
180
+ if ( in_radius ) {
181
+ const toStore = AccountTrieNodeRetrieval . serialize ( {
182
+ node : rlp ,
183
+ } )
184
+ await this . stateDB . storeContent ( contentKey , toStore )
185
+ }
186
+
187
+ for ( const { content, contentKey } of gossipContents ) {
188
+ await this . gossipContent ( contentKey , content )
189
+ }
190
+ }
191
+ return gossipContents
192
+ }
138
193
}
0 commit comments