@@ -166,6 +166,76 @@ const validateTerminalBlock = async (block: Block, chain: Chain): Promise<boolea
166166 return blockTd . gte ( ttd ) && parentBlockTd . lt ( ttd )
167167}
168168
169+ /**
170+ * Returns a block from a payload.
171+ * If errors, returns {@link PayloadStatusV1}
172+ */
173+ const assembleBlock = async (
174+ payload : ExecutionPayloadV1 ,
175+ chain : Chain
176+ ) : Promise < { block ?: Block ; error ?: PayloadStatusV1 } > => {
177+ const {
178+ blockNumber : number ,
179+ receiptsRoot : receiptTrie ,
180+ prevRandao : mixHash ,
181+ feeRecipient : coinbase ,
182+ transactions,
183+ } = payload
184+ const { config } = chain
185+ const { chainCommon : common } = config
186+
187+ const txs = [ ]
188+ for ( const [ index , serializedTx ] of transactions . entries ( ) ) {
189+ try {
190+ const tx = TransactionFactory . fromSerializedData ( toBuffer ( serializedTx ) , { common } )
191+ txs . push ( tx )
192+ } catch ( error ) {
193+ const validationError = `Invalid tx at index ${ index } : ${ error } `
194+ config . logger . error ( validationError )
195+ const latestValidHash = await validHash ( toBuffer ( payload . parentHash ) , chain )
196+ const response = { status : Status . INVALID , latestValidHash, validationError }
197+ return { error : response }
198+ }
199+ }
200+
201+ const transactionsTrie = await txsTrieRoot ( txs )
202+ const header : HeaderData = {
203+ ...payload ,
204+ number,
205+ receiptTrie,
206+ transactionsTrie,
207+ mixHash,
208+ coinbase,
209+ }
210+
211+ let block : Block
212+ try {
213+ block = Block . fromBlockData (
214+ { header, transactions : txs } ,
215+ { common, hardforkByTD : chain . headers . td }
216+ )
217+
218+ // Verify blockHash matches payload
219+ if ( ! block . hash ( ) . equals ( toBuffer ( payload . blockHash ) ) ) {
220+ const validationError = `Invalid blockHash, expected: ${
221+ payload . blockHash
222+ } , received: ${ bufferToHex ( block . hash ( ) ) } `
223+ config . logger . debug ( validationError )
224+ const latestValidHash = await validHash ( toBuffer ( header . parentHash ) , chain )
225+ const response = { status : Status . INVALID_BLOCK_HASH , latestValidHash, validationError }
226+ return { error : response }
227+ }
228+ } catch ( error ) {
229+ const validationError = `Error verifying block during init: ${ error } `
230+ config . logger . debug ( validationError )
231+ const latestValidHash = await validHash ( toBuffer ( header . parentHash ) , chain )
232+ const response = { status : Status . INVALID , latestValidHash, validationError }
233+ return { error : response }
234+ }
235+
236+ return { block }
237+ }
238+
169239/**
170240 * engine_* RPC module
171241 * @memberof module:rpc/modules
@@ -180,6 +250,7 @@ export class Engine {
180250 private vm : VM
181251 private txPool : TxPool
182252 private pendingBlock : PendingBlock
253+ private remoteBlocks : Map < String , Block >
183254 private connectionManager : CLConnectionManager
184255
185256 /**
@@ -200,6 +271,7 @@ export class Engine {
200271 this . txPool = ( this . service . synchronizer as FullSynchronizer ) . txPool
201272 this . connectionManager = new CLConnectionManager ( { config : this . chain . config } )
202273 this . pendingBlock = new PendingBlock ( { config : this . config , txPool : this . txPool } )
274+ this . remoteBlocks = new Map ( )
203275
204276 this . newPayloadV1 = middleware ( this . newPayloadV1 . bind ( this ) , 1 , [
205277 [
@@ -277,17 +349,21 @@ export class Engine {
277349 * 3. validationError: String|null - validation error message
278350 */
279351 async newPayloadV1 ( params : [ ExecutionPayloadV1 ] ) : Promise < PayloadStatusV1 > {
280- const [ payloadData ] = params
281- const {
282- blockNumber : number ,
283- receiptsRoot : receiptTrie ,
284- prevRandao : mixHash ,
285- feeRecipient : coinbase ,
286- transactions,
287- parentHash,
288- blockHash,
289- } = payloadData
290- const { chainCommon : common } = this . config
352+ const [ payload ] = params
353+ const { parentHash, blockHash } = payload
354+
355+ const { block, error } = await assembleBlock ( payload , this . chain )
356+ if ( ! block || error ) {
357+ let response = error
358+ if ( ! response ) {
359+ const validationError = `Error assembling block during init`
360+ this . config . logger . debug ( validationError )
361+ const latestValidHash = await validHash ( toBuffer ( payload . parentHash ) , this . chain )
362+ response = { status : Status . INVALID , latestValidHash, validationError }
363+ }
364+ this . connectionManager . lastNewPayload ( { payload, response } )
365+ return response
366+ }
291367
292368 const blockExists = await validHash ( toBuffer ( blockHash ) , this . chain )
293369 if ( blockExists ) {
@@ -301,9 +377,9 @@ export class Engine {
301377 }
302378
303379 try {
304- const block = await this . chain . getBlock ( toBuffer ( parentHash ) )
305- if ( ! block . _common . gteHardfork ( Hardfork . Merge ) ) {
306- const validTerminalBlock = await validateTerminalBlock ( block , this . chain )
380+ const parent = await this . chain . getBlock ( toBuffer ( parentHash ) )
381+ if ( ! parent . _common . gteHardfork ( Hardfork . Merge ) ) {
382+ const validTerminalBlock = await validateTerminalBlock ( parent , this . chain )
307383 if ( ! validTerminalBlock ) {
308384 const response = {
309385 status : Status . INVALID_TERMINAL_BLOCK ,
@@ -315,65 +391,15 @@ export class Engine {
315391 }
316392 }
317393 } catch ( error : any ) {
394+ // Stash the block for a potential forced forkchoice update to it later.
395+ this . remoteBlocks . set ( block . hash ( ) . toString ( 'hex' ) , block )
318396 // TODO if we can't find the parent and the block doesn't extend the canonical chain,
319397 // return ACCEPTED when optimistic sync is supported to store the block for later processing
320398 const response = { status : Status . SYNCING , validationError : null , latestValidHash : null }
321399 this . connectionManager . lastNewPayload ( { payload : params [ 0 ] , response } )
322400 return response
323401 }
324402
325- const txs = [ ]
326- for ( const [ index , serializedTx ] of transactions . entries ( ) ) {
327- try {
328- const tx = TransactionFactory . fromSerializedData ( toBuffer ( serializedTx ) , { common } )
329- txs . push ( tx )
330- } catch ( error ) {
331- const validationError = `Invalid tx at index ${ index } : ${ error } `
332- this . config . logger . error ( validationError )
333- const latestValidHash = await validHash ( toBuffer ( payloadData . parentHash ) , this . chain )
334- const response = { status : Status . INVALID , latestValidHash, validationError }
335- this . connectionManager . lastNewPayload ( { payload : params [ 0 ] , response } )
336- return response
337- }
338- }
339-
340- const transactionsTrie = await txsTrieRoot ( txs )
341- const header : HeaderData = {
342- ...payloadData ,
343- number,
344- receiptTrie,
345- transactionsTrie,
346- mixHash,
347- coinbase,
348- }
349-
350- let block : Block
351- try {
352- block = Block . fromBlockData (
353- { header, transactions : txs } ,
354- { common, hardforkByTD : this . chain . headers . td }
355- )
356-
357- // Verify blockHash matches payload
358- if ( ! block . hash ( ) . equals ( toBuffer ( payloadData . blockHash ) ) ) {
359- const validationError = `Invalid blockHash, expected: ${
360- payloadData . blockHash
361- } , received: ${ bufferToHex ( block . hash ( ) ) } `
362- this . config . logger . debug ( validationError )
363- const latestValidHash = await validHash ( toBuffer ( header . parentHash ) , this . chain )
364- const response = { status : Status . INVALID_BLOCK_HASH , latestValidHash, validationError }
365- this . connectionManager . lastNewPayload ( { payload : params [ 0 ] , response } )
366- return response
367- }
368- } catch ( error ) {
369- const validationError = `Error verifying block during init: ${ error } `
370- this . config . logger . debug ( validationError )
371- const latestValidHash = await validHash ( toBuffer ( header . parentHash ) , this . chain )
372- const response = { status : Status . INVALID , latestValidHash, validationError }
373- this . connectionManager . lastNewPayload ( { payload : params [ 0 ] , response } )
374- return response
375- }
376-
377403 const vmHead = this . chain . headers . latest !
378404 let blocks : Block [ ]
379405 try {
@@ -441,14 +467,20 @@ export class Engine {
441467 try {
442468 headBlock = await this . chain . getBlock ( toBuffer ( headBlockHash ) )
443469 } catch ( error ) {
444- const latestValidHash = bufferToHex ( this . chain . headers . latest ! . hash ( ) )
445- const payloadStatus = { status : Status . SYNCING , latestValidHash, validationError : null }
446- const response = { payloadStatus, payloadId : null }
447- this . connectionManager . lastForkchoiceUpdate ( {
448- state : params [ 0 ] ,
449- response,
450- } )
451- return response
470+ headBlock = this . remoteBlocks . get ( headBlockHash . slice ( 2 ) ) as Block
471+ if ( ! headBlock ) {
472+ const payloadStatus = {
473+ status : Status . SYNCING ,
474+ latestValidHash : null ,
475+ validationError : null ,
476+ }
477+ const response = { payloadStatus, payloadId : null }
478+ this . connectionManager . lastForkchoiceUpdate ( {
479+ state : params [ 0 ] ,
480+ response,
481+ } )
482+ return response
483+ }
452484 }
453485
454486 if ( ! headBlock . _common . gteHardfork ( Hardfork . Merge ) ) {
@@ -498,8 +530,11 @@ export class Engine {
498530 this . chain
499531 )
500532 } catch ( error ) {
501- const latestValidHash = await validHash ( headBlock . header . parentHash , this . chain )
502- const payloadStatus = { status : Status . SYNCING , latestValidHash, validationError : null }
533+ const payloadStatus = {
534+ status : Status . SYNCING ,
535+ latestValidHash : null ,
536+ validationError : null ,
537+ }
503538 const response = { payloadStatus, payloadId : null }
504539 this . connectionManager . lastForkchoiceUpdate ( {
505540 state : params [ 0 ] ,
@@ -510,7 +545,7 @@ export class Engine {
510545 }
511546
512547 const blocks = [ ...parentBlocks , headBlock ]
513- await this . execution . setHead ( headBlock )
548+ await this . execution . setHead ( blocks )
514549 this . txPool . removeNewBlockTxs ( blocks )
515550
516551 const timeDiff = new Date ( ) . getTime ( ) / 1000 - headBlock . header . timestamp . toNumber ( )
@@ -556,7 +591,7 @@ export class Engine {
556591 mixHash : prevRandao ,
557592 coinbase : suggestedFeeRecipient ,
558593 } )
559- const latestValidHash = await validHash ( headBlock . header . parentHash , this . chain )
594+ const latestValidHash = await validHash ( headBlock . hash ( ) , this . chain )
560595 const payloadStatus = { status : Status . VALID , latestValidHash, validationError : null }
561596 const response = { payloadStatus, payloadId : bufferToHex ( payloadId ) }
562597 this . connectionManager . lastForkchoiceUpdate ( {
@@ -567,7 +602,7 @@ export class Engine {
567602 return response
568603 }
569604
570- const latestValidHash = await validHash ( headBlock . header . hash ( ) , this . chain )
605+ const latestValidHash = await validHash ( headBlock . hash ( ) , this . chain )
571606 const payloadStatus = { status : Status . VALID , latestValidHash, validationError : null }
572607 const response = { payloadStatus, payloadId : null }
573608 this . connectionManager . lastForkchoiceUpdate ( {
0 commit comments