1
+ import assert from 'assert'
1
2
import { EventEmitter } from 'events'
2
3
import * as rlp from 'rlp'
3
4
import ms from 'ms'
@@ -18,6 +19,12 @@ export class ETH extends EventEmitter {
18
19
_statusTimeoutId : NodeJS . Timeout
19
20
_send : SendMethod
20
21
22
+ // Eth64
23
+ _hardfork : string = 'chainstart'
24
+ _latestBlock : number = 0
25
+ _forkHash : string = ''
26
+ _nextForkBlock : number = 0
27
+
21
28
constructor ( version : number , peer : Peer , send : SendMethod ) {
22
29
super ( )
23
30
@@ -30,10 +37,24 @@ export class ETH extends EventEmitter {
30
37
this . _statusTimeoutId = setTimeout ( ( ) => {
31
38
this . _peer . disconnect ( DISCONNECT_REASONS . TIMEOUT )
32
39
} , ms ( '5s' ) )
40
+
41
+ // Set forkHash and nextForkBlock
42
+ if ( this . _version >= 64 ) {
43
+ const c = this . _peer . _common
44
+ this . _hardfork = c . hardfork ( ) ? ( c . hardfork ( ) as string ) : this . _hardfork
45
+ // Set latestBlock minimally to start block of fork to have some more
46
+ // accurate basis if no latestBlock is provided along status send
47
+ this . _latestBlock = c . hardforkBlock ( this . _hardfork )
48
+ this . _forkHash = c . forkHash ( this . _hardfork )
49
+ // Next fork block number or 0 if none available
50
+ const nextForkBlock = c . nextHardforkBlock ( this . _hardfork )
51
+ this . _nextForkBlock = nextForkBlock ? nextForkBlock : 0
52
+ }
33
53
}
34
54
35
55
static eth62 = { name : 'eth' , version : 62 , length : 8 , constructor : ETH }
36
56
static eth63 = { name : 'eth' , version : 63 , length : 17 , constructor : ETH }
57
+ static eth64 = { name : 'eth' , version : 64 , length : 29 , constructor : ETH }
37
58
38
59
_handleMessage ( code : ETH . MESSAGE_CODES , data : any ) {
39
60
const payload = rlp . decode ( data ) as unknown
@@ -80,6 +101,41 @@ export class ETH extends EventEmitter {
80
101
this . emit ( 'message' , code , payload )
81
102
}
82
103
104
+ /**
105
+ * Eth 64 Fork ID validation (EIP-2124)
106
+ * @param forkId Remote fork ID
107
+ */
108
+ _validateForkId ( forkId : Buffer [ ] ) {
109
+ const c = this . _peer . _common
110
+
111
+ const peerForkHash = `0x${ forkId [ 0 ] . toString ( 'hex' ) } `
112
+ const peerNextFork = buffer2int ( forkId [ 1 ] )
113
+
114
+ if ( this . _forkHash === peerForkHash ) {
115
+ if ( peerNextFork ) {
116
+ if ( this . _latestBlock >= peerNextFork ) {
117
+ const msg = 'Remote is advertising a future fork that passed locally'
118
+ debug ( msg )
119
+ throw new assert . AssertionError ( { message : msg } )
120
+ }
121
+ }
122
+ }
123
+ const peerFork : any = c . hardforkForForkHash ( peerForkHash )
124
+ if ( peerFork === null ) {
125
+ const msg = 'Unknown fork hash'
126
+ debug ( msg )
127
+ throw new assert . AssertionError ( { message : msg } )
128
+ }
129
+
130
+ if ( ! c . hardforkGteHardfork ( peerFork . name , this . _hardfork ) ) {
131
+ if ( peerNextFork === null || c . nextHardforkBlock ( peerFork . name ) !== peerNextFork ) {
132
+ const msg = 'Outdated fork status, remote needs software update'
133
+ debug ( msg )
134
+ throw new assert . AssertionError ( { message : msg } )
135
+ }
136
+ }
137
+ }
138
+
83
139
_handleStatus ( ) : void {
84
140
if ( this . _status === null || this . _peerStatus === null ) return
85
141
clearTimeout ( this . _statusTimeoutId )
@@ -88,26 +144,48 @@ export class ETH extends EventEmitter {
88
144
assertEq ( this . _status [ 1 ] , this . _peerStatus [ 1 ] , 'NetworkId mismatch' , debug )
89
145
assertEq ( this . _status [ 4 ] , this . _peerStatus [ 4 ] , 'Genesis block mismatch' , debug )
90
146
91
- this . emit ( ' status' , {
147
+ let status : any = {
92
148
networkId : this . _peerStatus [ 1 ] ,
93
149
td : Buffer . from ( this . _peerStatus [ 2 ] ) ,
94
150
bestHash : Buffer . from ( this . _peerStatus [ 3 ] ) ,
95
- genesisHash : Buffer . from ( this . _peerStatus [ 4 ] )
96
- } )
151
+ genesisHash : Buffer . from ( this . _peerStatus [ 4 ] ) ,
152
+ }
153
+
154
+ if ( this . _version >= 64 ) {
155
+ assertEq ( this . _peerStatus [ 5 ] . length , 2 , 'Incorrect forkId msg format' , debug )
156
+ this . _validateForkId ( this . _peerStatus [ 5 ] as Buffer [ ] )
157
+ console . log ( `Successful Eth64 validation with ${ this . _peer . _socket . remoteAddress } ` )
158
+ status [ 'forkId' ] = this . _peerStatus [ 5 ]
159
+ }
160
+
161
+ this . emit ( 'status' , status )
97
162
}
98
163
99
164
getVersion ( ) {
100
165
return this . _version
101
166
}
102
167
168
+ _forkHashFromForkId ( forkId : Buffer ) : string {
169
+ return `0x${ forkId . toString ( 'hex' ) } `
170
+ }
171
+
172
+ _nextForkFromForkId ( forkId : Buffer ) : number {
173
+ return buffer2int ( forkId )
174
+ }
175
+
103
176
_getStatusString ( status : ETH . StatusMsg ) {
104
- let sStr = `[V:${ buffer2int ( status [ 0 ] ) } , NID: ${ buffer2int ( status [ 1 ] ) } , TD :${ buffer2int (
105
- status [ 2 ]
106
- ) } `
177
+ let sStr = `[V:${ buffer2int ( status [ 0 ] as Buffer ) } , NID :${ buffer2int (
178
+ status [ 1 ] as Buffer ,
179
+ ) } , TD: ${ buffer2int ( status [ 2 ] as Buffer ) } `
107
180
sStr += `, BestH:${ formatLogId ( status [ 3 ] . toString ( 'hex' ) , verbose ) } , GenH:${ formatLogId (
108
181
status [ 4 ] . toString ( 'hex' ) ,
109
- verbose
110
- ) } ]`
182
+ verbose ,
183
+ ) } `
184
+ if ( this . _version >= 64 ) {
185
+ sStr += `, ForkHash: 0x${ ( status [ 5 ] [ 0 ] as Buffer ) . toString ( 'hex' ) } `
186
+ sStr += `, ForkNext: ${ buffer2int ( status [ 5 ] [ 1 ] as Buffer ) } `
187
+ }
188
+ sStr += `]`
111
189
return sStr
112
190
}
113
191
@@ -120,6 +198,19 @@ export class ETH extends EventEmitter {
120
198
status . bestHash ,
121
199
status . genesisHash
122
200
]
201
+ if ( this . _version >= 64 ) {
202
+ if ( status . latestBlock ) {
203
+ if ( status . latestBlock < this . _latestBlock ) {
204
+ throw new Error (
205
+ 'latest block provided is not matching the HF setting of the Common instance (Rlpx)' ,
206
+ )
207
+ }
208
+ this . _latestBlock = status . latestBlock
209
+ }
210
+ const forkHashB = Buffer . from ( this . _forkHash . substr ( 2 ) , 'hex' )
211
+ const nextForkB = Buffer . from ( this . _nextForkBlock . toString ( 16 ) , 'hex' )
212
+ this . _status . push ( [ forkHashB , nextForkB ] )
213
+ }
123
214
124
215
debug (
125
216
`Send STATUS message to ${ this . _peer . _socket . remoteAddress } :${
@@ -171,20 +262,14 @@ export class ETH extends EventEmitter {
171
262
}
172
263
173
264
export namespace ETH {
174
- export type StatusMsg = {
175
- 0 : Buffer
176
- 1 : Buffer
177
- 2 : Buffer
178
- 3 : Buffer
179
- 4 : Buffer
180
- length : 5
181
- }
265
+ export interface StatusMsg extends Array < Buffer | Buffer [ ] > { }
182
266
183
267
export type StatusOpts = {
184
268
version : number
185
269
// networkId: number
186
270
td : Buffer
187
271
bestHash : Buffer
272
+ latestBlock ?: number
188
273
genesisHash : Buffer
189
274
}
190
275
0 commit comments