@@ -19,6 +19,8 @@ package catalyst
1919
2020import (
2121 "bytes"
22+ "crypto/sha256"
23+ "encoding/binary"
2224 "errors"
2325 "fmt"
2426 "math/big"
@@ -43,8 +45,9 @@ import (
4345
4446var (
4547 VALID = GenericStringResponse {"VALID" }
46- INVALID = GenericStringResponse {"INVALID" }
47- SYNCING = GenericStringResponse {"SYNCING" }
48+ SUCCESS = GenericStringResponse {"SUCCESS" }
49+ INVALID = ForkChoiceResponse {Status : "INVALID" , PayloadID : nil }
50+ SYNCING = ForkChoiceResponse {Status : "INVALID" , PayloadID : nil }
4851 UnknownHeader = rpc.CustomError {Code : - 32000 , Message : "unknown header" }
4952 UnknownPayload = rpc.CustomError {Code : - 32001 , Message : "unknown payload" }
5053)
@@ -82,7 +85,7 @@ type ConsensusAPI struct {
8285 eth * eth.Ethereum
8386 les * les.LightEthereum
8487 engine consensus.Engine // engine is the post-merge consensus engine, only for block creation
85- preparedBlocks map [int ] * ExecutableData
88+ preparedBlocks map [uint64 ] * ExecutableDataV1
8689}
8790
8891func NewConsensusAPI (eth * eth.Ethereum , les * les.LightEthereum ) * ConsensusAPI {
@@ -111,7 +114,7 @@ func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI {
111114 eth : eth ,
112115 les : les ,
113116 engine : engine ,
114- preparedBlocks : make (map [int ] * ExecutableData ),
117+ preparedBlocks : make (map [uint64 ] * ExecutableDataV1 ),
115118 }
116119}
117120
@@ -171,64 +174,87 @@ func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl
171174 return env , nil
172175}
173176
174- func (api * ConsensusAPI ) PreparePayload (params AssembleBlockParams ) (* PayloadResponse , error ) {
175- data , err := api .assembleBlock (params )
176- if err != nil {
177- return nil , err
178- }
179- id := len (api .preparedBlocks )
180- api .preparedBlocks [id ] = data
181- return & PayloadResponse {PayloadID : uint64 (id )}, nil
182- }
183-
184- func (api * ConsensusAPI ) GetPayload (PayloadID hexutil.Uint64 ) (* ExecutableData , error ) {
185- data , ok := api .preparedBlocks [int (PayloadID )]
177+ func (api * ConsensusAPI ) GetPayloadV1 (PayloadID hexutil.Bytes ) (* ExecutableDataV1 , error ) {
178+ hash := []byte (PayloadID )
179+ id := binary .BigEndian .Uint64 (hash [:8 ])
180+ data , ok := api .preparedBlocks [id ]
186181 if ! ok {
187182 return nil , & UnknownPayload
188183 }
189184 return data , nil
190185}
191186
192- // ConsensusValidated is called to mark a block as valid, so
193- // that data that is no longer needed can be removed.
194- func (api * ConsensusAPI ) ConsensusValidated (params ConsensusValidatedParams ) error {
195- switch params .Status {
196- case VALID .Status :
197- return nil
198- case INVALID .Status :
199- // TODO (MariusVanDerWijden) delete the block from the bc
200- return nil
201- default :
202- return errors .New ("invalid params.status" )
187+ func (api * ConsensusAPI ) ForkchoiceUpdatedV1 (heads ForkchoiceStateV1 , PayloadAttributes * PayloadAttributesV1 ) (ForkChoiceResponse , error ) {
188+ var emptyHash = common.Hash {}
189+ if ! bytes .Equal (heads .HeadBlockHash [:], emptyHash [:]) {
190+ if err := api .checkTerminalTotalDifficulty (heads .HeadBlockHash ); err != nil {
191+ if block := api .eth .BlockChain ().GetBlockByHash (heads .HeadBlockHash ); block == nil {
192+ // TODO (MariusVanDerWijden) trigger sync
193+ return SYNCING , nil
194+ }
195+ return INVALID , err
196+ }
197+ // If the finalized block is set, check if it is in our blockchain
198+ if ! bytes .Equal (heads .FinalizedBlockHash [:], emptyHash [:]) {
199+ if block := api .eth .BlockChain ().GetBlockByHash (heads .FinalizedBlockHash ); block == nil {
200+ // TODO (MariusVanDerWijden) trigger sync
201+ return SYNCING , nil
202+ }
203+ }
204+ // SetHead
205+ if err := api .setHead (heads .HeadBlockHash ); err != nil {
206+ return INVALID , err
207+ }
208+ // Assemble block (if needed)
209+ if PayloadAttributes != nil {
210+ data , err := api .assembleBlock (heads .HeadBlockHash , PayloadAttributes )
211+ if err != nil {
212+ return INVALID , err
213+ }
214+ hash := computePayloadId (heads .HeadBlockHash , PayloadAttributes )
215+ id := binary .BigEndian .Uint64 (hash )
216+ api .preparedBlocks [id ] = data
217+ log .Info ("Created payload" , "payloadid" , id )
218+ // TODO (MariusVanDerWijden) do something with the payloadID?
219+ hex := hexutil .Bytes (hash )
220+ return ForkChoiceResponse {Status : SUCCESS .Status , PayloadID : & hex }, nil
221+ }
203222 }
223+ return ForkChoiceResponse {Status : SUCCESS .Status , PayloadID : nil }, nil
204224}
205225
206- func (api * ConsensusAPI ) ForkchoiceUpdated (params ForkChoiceParams ) error {
207- var emptyHash = common.Hash {}
208- if ! bytes .Equal (params .HeadBlockHash [:], emptyHash [:]) {
209- if err := api .checkTerminalTotalDifficulty (params .HeadBlockHash ); err != nil {
210- return err
211- }
212- return api .setHead (params .HeadBlockHash )
226+ func computePayloadId (headBlockHash common.Hash , params * PayloadAttributesV1 ) []byte {
227+ input := make ([]byte , 32 + 8 + 32 + 20 )
228+ copy (input , headBlockHash [:])
229+ binary .BigEndian .PutUint64 (input [32 :], params .Timestamp )
230+ copy (input [40 :], params .Random [:])
231+ copy (input [72 :], params .FeeRecipient [:])
232+ hash := sha256 .Sum256 (input )
233+ return hash [:8 ]
234+ }
235+
236+ func (api * ConsensusAPI ) invalid () ExecutePayloadResponse {
237+ if api .light {
238+ return ExecutePayloadResponse {Status : INVALID .Status , LatestValidHash : api .les .BlockChain ().CurrentHeader ().Hash ()}
213239 }
214- return nil
240+ return ExecutePayloadResponse { Status : INVALID . Status , LatestValidHash : api . eth . BlockChain (). CurrentHeader (). Hash ()}
215241}
216242
217243// ExecutePayload creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
218- func (api * ConsensusAPI ) ExecutePayload (params ExecutableData ) (GenericStringResponse , error ) {
244+ func (api * ConsensusAPI ) ExecutePayloadV1 (params ExecutableDataV1 ) (ExecutePayloadResponse , error ) {
219245 block , err := ExecutableDataToBlock (params )
220246 if err != nil {
221- return INVALID , err
247+ return api . invalid () , err
222248 }
223249 if api .light {
224250 parent := api .les .BlockChain ().GetHeaderByHash (params .ParentHash )
225251 if parent == nil {
226- return INVALID , fmt .Errorf ("could not find parent %x" , params .ParentHash )
252+ return api . invalid () , fmt .Errorf ("could not find parent %x" , params .ParentHash )
227253 }
228254 if err = api .les .BlockChain ().InsertHeader (block .Header ()); err != nil {
229- return INVALID , err
255+ return api . invalid () , err
230256 }
231- return VALID , nil
257+ return ExecutePayloadResponse { Status : VALID . Status , LatestValidHash : block . Hash ()} , nil
232258 }
233259 if ! api .eth .BlockChain ().HasBlock (block .ParentHash (), block .NumberU64 ()- 1 ) {
234260 /*
@@ -237,37 +263,38 @@ func (api *ConsensusAPI) ExecutePayload(params ExecutableData) (GenericStringRes
237263 return SYNCING, err
238264 }
239265 */
240- return SYNCING , nil
266+ // TODO (MariusVanDerWijden) we should return nil here not empty hash
267+ return ExecutePayloadResponse {Status : SYNCING .Status , LatestValidHash : common.Hash {}}, nil
241268 }
242269 parent := api .eth .BlockChain ().GetBlockByHash (params .ParentHash )
243270 td := api .eth .BlockChain ().GetTd (parent .Hash (), block .NumberU64 ()- 1 )
244271 ttd := api .eth .BlockChain ().Config ().TerminalTotalDifficulty
245272 if td .Cmp (ttd ) < 0 {
246- return INVALID , fmt .Errorf ("can not execute payload on top of block with low td got: %v threshold %v" , td , ttd )
273+ return api . invalid () , fmt .Errorf ("can not execute payload on top of block with low td got: %v threshold %v" , td , ttd )
247274 }
248275 if err := api .eth .BlockChain ().InsertBlockWithoutSetHead (block ); err != nil {
249- return INVALID , err
276+ return api . invalid () , err
250277 }
251278 merger := api .merger ()
252279 if ! merger .TDDReached () {
253280 merger .ReachTTD ()
254281 }
255- return VALID , nil
282+ return ExecutePayloadResponse { Status : VALID . Status , LatestValidHash : block . Hash ()} , nil
256283}
257284
258285// AssembleBlock creates a new block, inserts it into the chain, and returns the "execution
259286// data" required for eth2 clients to process the new block.
260- func (api * ConsensusAPI ) assembleBlock (params AssembleBlockParams ) (* ExecutableData , error ) {
287+ func (api * ConsensusAPI ) assembleBlock (parentHash common. Hash , params * PayloadAttributesV1 ) (* ExecutableDataV1 , error ) {
261288 if api .light {
262289 return nil , errors .New ("not supported" )
263290 }
264- log .Info ("Producing block" , "parentHash" , params . ParentHash )
291+ log .Info ("Producing block" , "parentHash" , parentHash )
265292
266293 bc := api .eth .BlockChain ()
267- parent := bc .GetBlockByHash (params . ParentHash )
294+ parent := bc .GetBlockByHash (parentHash )
268295 if parent == nil {
269- log .Warn ("Cannot assemble block with parent hash to unknown block" , "parentHash" , params . ParentHash )
270- return nil , fmt .Errorf ("cannot assemble block with unknown parent %s" , params . ParentHash )
296+ log .Warn ("Cannot assemble block with parent hash to unknown block" , "parentHash" , parentHash )
297+ return nil , fmt .Errorf ("cannot assemble block with unknown parent %s" , parentHash )
271298 }
272299
273300 if params .Timestamp < parent .Time () {
@@ -376,7 +403,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
376403 return txs , nil
377404}
378405
379- func ExecutableDataToBlock (params ExecutableData ) (* types.Block , error ) {
406+ func ExecutableDataToBlock (params ExecutableDataV1 ) (* types.Block , error ) {
380407 txs , err := decodeTransactions (params .Transactions )
381408 if err != nil {
382409 return nil , err
@@ -410,8 +437,8 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
410437 return block , nil
411438}
412439
413- func BlockToExecutableData (block * types.Block , random common.Hash ) * ExecutableData {
414- return & ExecutableData {
440+ func BlockToExecutableData (block * types.Block , random common.Hash ) * ExecutableDataV1 {
441+ return & ExecutableDataV1 {
415442 BlockHash : block .Hash (),
416443 ParentHash : block .ParentHash (),
417444 Coinbase : block .Coinbase (),
@@ -447,11 +474,7 @@ func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
447474 if newHeadBlock == nil {
448475 return & UnknownHeader
449476 }
450- parent := api .eth .BlockChain ().GetBlockByHash (newHeadBlock .ParentHash ())
451- if parent == nil {
452- return fmt .Errorf ("parent unavailable: %v" , newHeadBlock .ParentHash ())
453- }
454- td := api .eth .BlockChain ().GetTd (parent .Hash (), parent .NumberU64 ())
477+ td := api .eth .BlockChain ().GetTd (newHeadBlock .Hash (), newHeadBlock .NumberU64 ())
455478 if td != nil && td .Cmp (api .eth .BlockChain ().Config ().TerminalTotalDifficulty ) < 0 {
456479 return errors .New ("total difficulty not reached yet" )
457480 }
@@ -460,11 +483,6 @@ func (api *ConsensusAPI) checkTerminalTotalDifficulty(head common.Hash) error {
460483
461484// setHead is called to perform a force choice.
462485func (api * ConsensusAPI ) setHead (newHead common.Hash ) error {
463- // Trigger the transition if it's the first `NewHead` event.
464- merger := api .merger ()
465- if ! merger .PoSFinalized () {
466- merger .FinalizePoS ()
467- }
468486 log .Info ("Setting head" , "head" , newHead )
469487 if api .light {
470488 headHeader := api .les .BlockChain ().CurrentHeader ()
@@ -478,10 +496,20 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error {
478496 if err := api .les .BlockChain ().SetChainHead (newHeadHeader ); err != nil {
479497 return err
480498 }
499+ // Trigger the transition if it's the first `NewHead` event.
500+ merger := api .merger ()
501+ if ! merger .PoSFinalized () {
502+ merger .FinalizePoS ()
503+ }
481504 return nil
482505 }
483506 headBlock := api .eth .BlockChain ().CurrentBlock ()
484507 if headBlock .Hash () == newHead {
508+ // Trigger the transition if it's the first `NewHead` event.
509+ merger := api .merger ()
510+ if ! merger .PoSFinalized () {
511+ merger .FinalizePoS ()
512+ }
485513 return nil
486514 }
487515 newHeadBlock := api .eth .BlockChain ().GetBlockByHash (newHead )
@@ -491,6 +519,12 @@ func (api *ConsensusAPI) setHead(newHead common.Hash) error {
491519 if err := api .eth .BlockChain ().SetChainHead (newHeadBlock ); err != nil {
492520 return err
493521 }
522+ // Trigger the transition if it's the first `NewHead` event.
523+ merger := api .merger ()
524+ if ! merger .PoSFinalized () {
525+ merger .FinalizePoS ()
526+ }
527+ // TODO (MariusVanDerWijden) are we really synced now?
494528 api .eth .SetSynced ()
495529 return nil
496530}
0 commit comments