From 1d4e02f7abe4eb9a7c385734bc36d4c3bd39e21f Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 13 Jan 2022 15:36:24 +0100 Subject: [PATCH] eth/catalyst: evict old payloads, type PayloadID --- eth/catalyst/api.go | 40 ++++++++++++++++++------------------- eth/catalyst/api_test.go | 5 ++--- eth/catalyst/api_types.go | 35 ++++++++++++++++++++------------ eth/catalyst/gen_payload.go | 36 --------------------------------- 4 files changed, 44 insertions(+), 72 deletions(-) delete mode 100644 eth/catalyst/gen_payload.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 1c3d65a1ce79e..debb44b1725f8 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -25,8 +25,9 @@ import ( "math/big" "time" + lru "github.com/hashicorp/golang-lru" + "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/misc" @@ -50,9 +51,10 @@ var ( GenericServerError = rpc.CustomError{Code: -32000, ValidationError: "Server error"} UnknownPayload = rpc.CustomError{Code: -32001, ValidationError: "Unknown payload"} InvalidTB = rpc.CustomError{Code: -32002, ValidationError: "Invalid terminal block"} - InvalidPayloadID = rpc.CustomError{Code: 1, ValidationError: "invalid payload id"} ) +const preparedPayloadsCacheSize = 10 + // Register adds catalyst APIs to the full node. func Register(stack *node.Node, backend *eth.Ethereum) error { log.Warn("Catalyst mode enabled", "protocol", "eth") @@ -86,7 +88,8 @@ type ConsensusAPI struct { eth *eth.Ethereum les *les.LightEthereum engine consensus.Engine // engine is the post-merge consensus engine, only for block creation - preparedBlocks map[uint64]*ExecutableDataV1 + preparedBlocks *lru.Cache // preparedBlocks caches payloads (*ExecutableDataV1) by payload ID (PayloadID) + } func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { @@ -110,12 +113,16 @@ func NewConsensusAPI(eth *eth.Ethereum, les *les.LightEthereum) *ConsensusAPI { engine = beacon.New(eth.Engine()) } } + preparedBlocks, _ := lru.NewWithEvict(preparedPayloadsCacheSize, func(key, value interface{}) { + log.Trace("evicted payload from preparedBlocks", "payloadid", key.(PayloadID), + "blockhash", value.(*ExecutableDataV1).BlockHash) + }) // positive cache size, no error return &ConsensusAPI{ light: eth == nil, eth: eth, les: les, engine: engine, - preparedBlocks: make(map[uint64]*ExecutableDataV1), + preparedBlocks: preparedBlocks, } } @@ -175,17 +182,12 @@ func (api *ConsensusAPI) makeEnv(parent *types.Block, header *types.Header) (*bl return env, nil } -func (api *ConsensusAPI) GetPayloadV1(payloadID hexutil.Bytes) (*ExecutableDataV1, error) { - hash := []byte(payloadID) - if len(hash) < 8 { - return nil, &InvalidPayloadID - } - id := binary.BigEndian.Uint64(hash[:8]) - data, ok := api.preparedBlocks[id] +func (api *ConsensusAPI) GetPayloadV1(payloadID PayloadID) (*ExecutableDataV1, error) { + data, ok := api.preparedBlocks.Get(payloadID) if !ok { return nil, &UnknownPayload } - return data, nil + return data.(*ExecutableDataV1), nil } func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads ForkchoiceStateV1, PayloadAttributes *PayloadAttributesV1) (ForkChoiceResponse, error) { @@ -216,25 +218,23 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads ForkchoiceStateV1, PayloadAtt if err != nil { return INVALID, err } - hash := computePayloadId(heads.HeadBlockHash, PayloadAttributes) - id := binary.BigEndian.Uint64(hash) - api.preparedBlocks[id] = data + id := computePayloadId(heads.HeadBlockHash, PayloadAttributes) + api.preparedBlocks.Add(id, data) log.Info("Created payload", "payloadid", id) - // TODO (MariusVanDerWijden) do something with the payloadID? - hex := hexutil.Bytes(hash) - return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: &hex}, nil + return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: &id}, nil } return ForkChoiceResponse{Status: SUCCESS.Status, PayloadID: nil}, nil } -func computePayloadId(headBlockHash common.Hash, params *PayloadAttributesV1) []byte { +func computePayloadId(headBlockHash common.Hash, params *PayloadAttributesV1) (out PayloadID) { // Hash hasher := sha256.New() hasher.Write(headBlockHash[:]) binary.Write(hasher, binary.BigEndian, params.Timestamp) hasher.Write(params.Random[:]) hasher.Write(params.SuggestedFeeRecipient[:]) - return hasher.Sum([]byte{})[:8] + copy(out[:], hasher.Sum(nil)[:8]) + return } func (api *ConsensusAPI) invalid() ExecutePayloadResponse { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 6e52c4fea27d7..aa5cb80eef1d3 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -22,7 +22,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -158,7 +157,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { t.Fatalf("error preparing payload, err=%v", err) } payloadID := computePayloadId(fcState.HeadBlockHash, &blockParams) - execData, err := api.GetPayloadV1(hexutil.Bytes(payloadID)) + execData, err := api.GetPayloadV1(payloadID) if err != nil { t.Fatalf("error getting payload, err=%v", err) } @@ -391,7 +390,7 @@ func TestFullAPI(t *testing.T) { t.Fatalf("error preparing payload, invalid status: %v", resp.Status) } payloadID := computePayloadId(parent.Hash(), ¶ms) - payload, err := api.GetPayloadV1(hexutil.Bytes(payloadID)) + payload, err := api.GetPayloadV1(payloadID) if err != nil { t.Fatalf("can't get payload: %v", err) } diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 1f6703030a7c7..15a8ad2c3ed10 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -17,6 +17,7 @@ package catalyst import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -69,17 +70,6 @@ type executableDataMarshaling struct { Transactions []hexutil.Bytes } -//go:generate go run github.com/fjl/gencodec -type PayloadResponse -field-override payloadResponseMarshaling -out gen_payload.go - -type PayloadResponse struct { - PayloadID uint64 `json:"payloadId"` -} - -// JSON type overrides for payloadResponse. -type payloadResponseMarshaling struct { - PayloadID hexutil.Uint64 -} - type NewBlockResponse struct { Valid bool `json:"valid"` } @@ -102,9 +92,28 @@ type ConsensusValidatedParams struct { Status string `json:"status"` } +// PayloadID is an identifier of the payload build process +type PayloadID [8]byte + +func (b PayloadID) String() string { + return hexutil.Encode(b[:]) +} + +func (b PayloadID) MarshalText() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalText() +} + +func (b *PayloadID) UnmarshalText(input []byte) error { + err := hexutil.UnmarshalFixedText("PayloadID", input, b[:]) + if err != nil { + return fmt.Errorf("invalid payload id %q: %w", input, err) + } + return nil +} + type ForkChoiceResponse struct { - Status string `json:"status"` - PayloadID *hexutil.Bytes `json:"payloadId"` + Status string `json:"status"` + PayloadID *PayloadID `json:"payloadId"` } type ForkchoiceStateV1 struct { diff --git a/eth/catalyst/gen_payload.go b/eth/catalyst/gen_payload.go deleted file mode 100644 index a0b00fcfd8c3b..0000000000000 --- a/eth/catalyst/gen_payload.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package catalyst - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*payloadResponseMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (p PayloadResponse) MarshalJSON() ([]byte, error) { - type PayloadResponse struct { - PayloadID hexutil.Uint64 `json:"payloadId"` - } - var enc PayloadResponse - enc.PayloadID = hexutil.Uint64(p.PayloadID) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (p *PayloadResponse) UnmarshalJSON(input []byte) error { - type PayloadResponse struct { - PayloadID *hexutil.Uint64 `json:"payloadId"` - } - var dec PayloadResponse - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.PayloadID != nil { - p.PayloadID = uint64(*dec.PayloadID) - } - return nil -}