From b946852a660d1b2d3c7f573602974672befe3d00 Mon Sep 17 00:00:00 2001 From: itsdevbear Date: Tue, 20 Feb 2024 18:24:29 -0500 Subject: [PATCH] basic tests --- beacon/engine/gen_epe.go | 3 + beacon/engine/types.go | 20 ++ eth/catalyst/api.go | 4 +- ethclient/engineclient/engineclient.go | 226 +++++++----- ethclient/engineclient/engineclient_test.go | 373 ++++++++++++++++++++ ethclient/engineclient/types.go | 23 ++ ethclient/ethclient.go | 3 - signer/fourbyte/4byte.json | 4 +- 8 files changed, 569 insertions(+), 87 deletions(-) create mode 100644 ethclient/engineclient/engineclient_test.go create mode 100644 ethclient/engineclient/types.go diff --git a/beacon/engine/gen_epe.go b/beacon/engine/gen_epe.go index e69f9a5951..8a03cd1f5f 100644 --- a/beacon/engine/gen_epe.go +++ b/beacon/engine/gen_epe.go @@ -37,9 +37,12 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { Override *bool `json:"shouldOverrideBuilder"` } var dec ExecutionPayloadEnvelope + panic(input) + panic("unreachable: generated code should never panic") if err := json.Unmarshal(input, &dec); err != nil { return err } + if dec.ExecutionPayload == nil { return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope") } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 60accc3c79..40670cca42 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -17,6 +17,7 @@ package engine import ( + "encoding/json" "fmt" "math/big" @@ -156,6 +157,25 @@ func (b *PayloadID) UnmarshalText(input []byte) error { return nil } +// MarshalJSON marshals the PayloadIDBytes to hex. +// It is required since we can't use struct tags for [8]byte. +func (b PayloadID) MarshalJSON() ([]byte, error) { + return json.Marshal(hexutil.Bytes(b[:])) +} + +// UnmarshalJSON unmarshals the PayloadID from hex. +func (b *PayloadID) UnmarshalJSON(input []byte) error { + var dec hexutil.Bytes + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if len(dec) != len(b) { + return fmt.Errorf("invalid payload id %q", dec) + } + copy(b[:], dec) + return nil +} + type ForkChoiceResponse struct { PayloadStatus PayloadStatusV1 `json:"payloadStatus"` PayloadID *PayloadID `json:"payloadId"` diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 44518612e8..1742403125 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -77,7 +77,7 @@ const ( ) // All methods provided over the engine endpoint. -var caps = []string{ +var Caps = []string{ "engine_forkchoiceUpdatedV1", "engine_forkchoiceUpdatedV2", "engine_forkchoiceUpdatedV3", @@ -813,7 +813,7 @@ func (api *ConsensusAPI) heartbeat() { // ExchangeCapabilities returns the current methods provided by this node. func (api *ConsensusAPI) ExchangeCapabilities([]string) []string { - return caps + return Caps } // GetClientVersionV1 exchanges client version data of this node. diff --git a/ethclient/engineclient/engineclient.go b/ethclient/engineclient/engineclient.go index 6eb2ef010d..e9055651db 100644 --- a/ethclient/engineclient/engineclient.go +++ b/ethclient/engineclient/engineclient.go @@ -9,124 +9,162 @@ // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// GNU Lesser General Public License for more detailc. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package engineclient provides an RPC client for engine API required functions. +// Package engineclient provides an RPC client for engine API required functionc. package engineclient import ( "context" - "encoding/json" "errors" + "fmt" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/ethclient" ) // Client is a wrapper around rpc.Client that implements geth-specific functionality. // // If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead. type Client struct { - c *rpc.Client + *ethclient.Client } // New creates a client that uses the given RPC client. -func New(c *rpc.Client) *Client { +func New(c *ethclient.Client) *Client { return &Client{c} } -// PayloadIDBytes defines a custom type for Payload IDs used by the engine API -// client with proper JSON Marshal and Unmarshal methods to hex. -type PayloadIDBytes [8]byte - -// MarshalJSON -- -func (b PayloadIDBytes) MarshalJSON() ([]byte, error) { - return json.Marshal(hexutil.Bytes(b[:])) -} - -// ForkchoiceUpdatedResponse is the response kind received by the -// engine_forkchoiceUpdatedV1 endpoint. -type ForkchoiceUpdatedResponse struct { - Status *engine.PayloadStatusV1 `json:"payloadStatus"` - PayloadId *PayloadIDBytes `json:"payloadId"` - ValidationError string `json:"validationError"` -} - -// NewPayloadV3 calls the engine_newPayloadV3 method via JSON-RPC. -func (s *Client) NewPayloadV3( - ctx context.Context, payload *engine.ExecutionPayloadEnvelope, - versionedHashes []common.Hash, parentBlockRoot *common.Hash, -) (*engine.PayloadStatusV1, error) { - return s.newPayload(ctx, "engine_newPayloadV3", payload, versionedHashes, parentBlockRoot) -} - -// NewPayloadV2 calls the engine_newPayloadV2 method via JSON-RPC. -func (s *Client) NewPayloadV2( - ctx context.Context, payload *engine.ExecutionPayloadEnvelope, -) (*engine.PayloadStatusV1, error) { - return s.newPayload(ctx, "engine_newPayloadV2", payload) -} - -// NewPayloadV1 calls the engine_newPayloadV1 method via JSON-RPC. -func (s *Client) NewPayloadV1( - ctx context.Context, payload *engine.ExecutionPayloadEnvelope, -) (*engine.PayloadStatusV1, error) { - return s.newPayload(ctx, "engine_newPayloadV1", payload) -} - -func (s *Client) newPayload( - ctx context.Context, method string, payload *engine.ExecutionPayloadEnvelope, args ...any, -) (*engine.PayloadStatusV1, error) { - result := &engine.PayloadStatusV1{} - if err := s.c.CallContext( - ctx, result, method, payload, args, +// ExchangeTransitionConfigurationV1 calls the engine_exchangeTransitionConfigurationV1 +// method via JSON-RPC. This is not really needed anymore, since we are post merge, +// but it is still here for reference / completeness sake. +func (c *Client) ExchangeTransitionConfigurationV1( + ctx context.Context, + config engine.TransitionConfigurationV1, +) (*engine.TransitionConfigurationV1, error) { + result := &engine.TransitionConfigurationV1{} + if err := c.Client.Client().CallContext( + ctx, result, "engine_exchangeTransitionConfigurationV1", config, ); err != nil { return nil, err } return result, nil } -// ForkchoiceUpdatedV3 calls the engine_forkchoiceUpdatedV3 method via JSON-RPC. -func (s *Client) ForkchoiceUpdatedV3( - ctx context.Context, state *engine.ForkchoiceStateV1, attrs *engine.PayloadAttributes, -) (*ForkchoiceUpdatedResponse, error) { - return s.forkchoiceUpdated(ctx, "engine_forkchoiceUpdatedV3", state, attrs) +// ExchangeCapabilities calls the engine_exchangeCapabilities method via JSON-RPC. +func (c *Client) ExchangeCapabilities( + ctx context.Context, + capabilities []string, +) ([]string, error) { + result := make([]string, 0) + if err := c.Client.Client().CallContext( + ctx, &result, "engine_exchangeCapabilities", &capabilities, + ); err != nil { + return nil, err + } + return result, nil } -// ForkchoiceUpdatedV2 calls the engine_forkchoiceUpdatedV2 method via JSON-RPC. -func (s *Client) ForkchoiceUpdatedV2( - ctx context.Context, state *engine.ForkchoiceStateV1, attrs *engine.PayloadAttributes, -) (*ForkchoiceUpdatedResponse, error) { - return s.forkchoiceUpdated(ctx, "engine_forkchoiceUpdatedV2", state, attrs) +// GetClientVersionV1 calls the engine_getClientVersionV1 method via JSON-RPC. +func (c *Client) GetClientVersionV1(ctx context.Context) ([]engine.ClientVersionV1, error) { + result := make([]engine.ClientVersionV1, 0) + if err := c.Client.Client().CallContext( + ctx, &result, "engine_getClientVersionV1", nil, + ); err != nil { + return nil, err + } + return result, nil +} + +// NewPayloadV3 calls the engine_newPayloadV3 method via JSON-RPC. +func (c *Client) NewPayloadV3( + ctx context.Context, payload *engine.ExecutionPayloadEnvelope, + versionedHashes common.Hash, parentBlockRoot common.Hash, +) (*engine.PayloadStatusV1, error) { + return c.newPayloadWithArgs(ctx, CancunV3, payload, versionedHashes, parentBlockRoot) +} + +// NewPayloadV2 calls the engine_newPayloadV2 method via JSON-RPC. +func (c *Client) NewPayloadV2( + ctx context.Context, payload *engine.ExecutionPayloadEnvelope, +) (*engine.PayloadStatusV1, error) { + return c.newPayload(ctx, ShanghaiV2, payload) +} + +// NewPayloadV1 calls the engine_newPayloadV1 method via JSON-RPC. +func (c *Client) NewPayloadV1( + ctx context.Context, payload *engine.ExecutionPayloadEnvelope, +) (*engine.PayloadStatusV1, error) { + return c.newPayload(ctx, ParisV1, payload) +} + +// newPayload is a helper function that can call an arbitrary version of the newPayload method. +func (c *Client) newPayload( + ctx context.Context, version APIVersion, payload *engine.ExecutionPayloadEnvelope, +) (*engine.PayloadStatusV1, error) { + result := &engine.PayloadStatusV1{} + if err := c.Client.Client().CallContext( + ctx, &result, fmt.Sprintf("engine_newPayloadV%d", version), payload.ExecutionPayload, + ); err != nil { + return nil, err + } + return result, nil +} + +// newPayloadWithArgs is a helper function that can call an arbitrary version of the newPayload method. +func (c *Client) newPayloadWithArgs( + ctx context.Context, version APIVersion, payload *engine.ExecutionPayloadEnvelope, args ...any, +) (*engine.PayloadStatusV1, error) { + result := &engine.PayloadStatusV1{} + if err := c.Client.Client().CallContext( + ctx, &result, fmt.Sprintf("engine_newPayloadV%d", version), payload.ExecutionPayload, args, + ); err != nil { + return nil, err + } + return result, nil } // ForkchoiceUpdatedV1 calls the engine_forkchoiceUpdatedV1 method via JSON-RPC. -func (s *Client) ForkchoiceUpdatedV1( +func (c *Client) ForkchoiceUpdatedV1( ctx context.Context, state *engine.ForkchoiceStateV1, attrs *engine.PayloadAttributes, ) (*ForkchoiceUpdatedResponse, error) { - return s.forkchoiceUpdated(ctx, "engine_forkchoiceUpdatedV1", state, attrs) + return c.forkchoiceUpdated(ctx, ParisV1, state, attrs) +} + +// ForkchoiceUpdatedV2 calls the engine_forkchoiceUpdatedV2 method via JSON-RPC. +func (c *Client) ForkchoiceUpdatedV2( + ctx context.Context, state *engine.ForkchoiceStateV1, attrs *engine.PayloadAttributes, +) (*ForkchoiceUpdatedResponse, error) { + return c.forkchoiceUpdated(ctx, ShanghaiV2, state, attrs) +} + +// ForkchoiceUpdatedV3 calls the engine_forkchoiceUpdatedV3 method via JSON-RPC. +func (c *Client) ForkchoiceUpdatedV3( + ctx context.Context, state *engine.ForkchoiceStateV1, attrs *engine.PayloadAttributes, +) (*ForkchoiceUpdatedResponse, error) { + return c.forkchoiceUpdated(ctx, CancunV3, state, attrs) } // forkchoiceUpdateCall is a helper function to call to any version of the forkchoiceUpdated // method. -func (s *Client) forkchoiceUpdated( - ctx context.Context, method string, state *engine.ForkchoiceStateV1, attrs any, +func (c *Client) forkchoiceUpdated( + ctx context.Context, version APIVersion, state *engine.ForkchoiceStateV1, attrs any, ) (*ForkchoiceUpdatedResponse, error) { result := &ForkchoiceUpdatedResponse{} - if err := s.c.CallContext( - ctx, result, method, state, attrs, + if err := c.Client.Client().CallContext( + ctx, result, fmt.Sprintf("engine_forkchoiceUpdatedV%d", version), state, attrs, ); err != nil { return nil, err } if result.Status == nil { - return nil, errors.New("got nil status in" + method) + return nil, fmt.Errorf("got nil status in engine_forkchoiceUpdatedV%d", version) } else if result.ValidationError != "" { return nil, errors.New(result.ValidationError) } @@ -135,30 +173,58 @@ func (s *Client) forkchoiceUpdated( } // GetPayloadV3 calls the engine_getPayloadV3 method via JSON-RPC. -func (s *Client) GetPayloadV3( - ctx context.Context, payloadID PayloadIDBytes, +func (c *Client) GetPayloadV1( + ctx context.Context, payloadID *engine.PayloadID, ) (*engine.ExecutionPayloadEnvelope, error) { - return s.getPayload(ctx, "engine_getPayloadV3", payloadID) + return c.getPayload(ctx, ParisV1, payloadID) } // GetPayloadV2 calls the engine_getPayloadV3 method via JSON-RPC. -func (s *Client) GetPayloadV2( - ctx context.Context, payloadID PayloadIDBytes, +func (c *Client) GetPayloadV2( + ctx context.Context, payloadID *engine.PayloadID, ) (*engine.ExecutionPayloadEnvelope, error) { - return s.getPayload(ctx, "engine_getPayloadV2", payloadID) + return c.getPayload(ctx, ShanghaiV2, payloadID) } // GetPayloadV3 calls the engine_getPayloadV3 method via JSON-RPC. -func (s *Client) GetPayloadV1( - ctx context.Context, payloadID PayloadIDBytes, +func (c *Client) GetPayloadV3( + ctx context.Context, payloadID *engine.PayloadID, ) (*engine.ExecutionPayloadEnvelope, error) { - return s.getPayload(ctx, "engine_getPayloadV1", payloadID) + return c.getPayload(ctx, CancunV3, payloadID) } -func (s *Client) getPayload(ctx context.Context, method string, payloadID PayloadIDBytes) (*engine.ExecutionPayloadEnvelope, error) { +// getPayload is a helper function that can call an arbitrary version of the getPayload method. +func (c *Client) getPayload(ctx context.Context, version APIVersion, payloadID *engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { result := &engine.ExecutionPayloadEnvelope{} - if err := s.c.CallContext( - ctx, result, method, payloadID, + if err := c.Client.Client().CallContext( + ctx, result, fmt.Sprintf("engine_getPayloadV%d", version), payloadID, + ); err != nil { + return nil, err + } + return result, nil +} + +// GetPayloadBodiesByHashV1 calls the engine_getPayloadBodiesByHashV1 method via JSON-RPC. +func (c *Client) GetPayloadBodiesByHashV1( + ctx context.Context, + hashes []common.Hash, +) ([]*engine.ExecutionPayloadBodyV1, error) { + result := make([]*engine.ExecutionPayloadBodyV1, 0) + if err := c.Client.Client().CallContext( + ctx, &result, "engine_getPayloadBodiesByHashV1", &hashes, + ); err != nil { + return nil, err + } + return result, nil +} + +// GetPayloadBodiesByRangeV1 calls the engine_getPayloadBodiesByRangeV1 method via JSON-RPC. +func (c *Client) GetPayloadBodiesByRangeV1( + ctx context.Context, start, count hexutil.Uint64, +) ([]*engine.ExecutionPayloadBodyV1, error) { + result := make([]*engine.ExecutionPayloadBodyV1, 0) + if err := c.Client.Client().CallContext( + ctx, &result, "engine_getPayloadBodiesByRangeV1", start, count, ); err != nil { return nil, err } diff --git a/ethclient/engineclient/engineclient_test.go b/ethclient/engineclient/engineclient_test.go new file mode 100644 index 0000000000..169c33440d --- /dev/null +++ b/ethclient/engineclient/engineclient_test.go @@ -0,0 +1,373 @@ +package engineclient + +import ( + "context" + "fmt" + "math/big" + "strings" + + "reflect" + "testing" + "time" + + "github.com/ethereum/go-ethereum/beacon/engine" + "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/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" +) + +var ( + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + testBalance = big.NewInt(2e15) +) + +var genesis = &core.Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: types.GenesisAlloc{testAddr: {Balance: testBalance}}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + BaseFee: big.NewInt(params.InitialBaseFee), +} + +var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ + Nonce: 0, + Value: big.NewInt(12), + GasPrice: big.NewInt(params.InitialBaseFee), + Gas: params.TxGas, + To: &common.Address{2}, +}) + +var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ + Nonce: 1, + Value: big.NewInt(8), + GasPrice: big.NewInt(params.InitialBaseFee), + Gas: params.TxGas, + To: &common.Address{2}, +}) + +func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { + // Generate test chain. + blocks := generateTestChain() + + // Create node + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatalf("can't create new node: %v", err) + } + // Create Ethereum Service + config := ðconfig.Config{Genesis: genesis} + ethservice, err := eth.New(n, config) + if err != nil { + t.Fatalf("can't create new ethereum service: %v", err) + } + + // Register the engine api namespace. + catalyst.Register(n, ethservice) + + // Import the test chain. + if err := n.Start(); err != nil { + t.Fatalf("can't start test node: %v", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { + t.Fatalf("can't import test blocks: %v", err) + } + // Ensure the tx indexing is fully generated + for ; ; time.Sleep(time.Millisecond * 100) { + progress, err := ethservice.BlockChain().TxIndexProgress() + if err == nil && progress.Done() { + break + } + } + return n, blocks +} + +func generateTestChain() []*types.Block { + generate := func(i int, g *core.BlockGen) { + g.OffsetTime(5) + g.SetExtra([]byte("test")) + if i == 1 { + // Test transactions are included in block #2. + g.AddTx(testTx1) + g.AddTx(testTx2) + } + } + _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 2, generate) + return append([]*types.Block{genesis.ToBlock()}, blocks...) +} + +func TestEngineClient(t *testing.T) { + backend, chain := newTestBackend(t) + client := New(ethclient.NewClient(backend.Attach())) + defer backend.Close() + defer client.Close() + + tests := map[string]struct { + test func(t *testing.T) + }{ + "ExchangeCapabilities": { + func(t *testing.T) { testExchangeCapabilities(t, chain, client) }, + }, + "GetClientVersionV1": { + func(t *testing.T) { testGetClientV1(t, chain, client) }, + }, + "GetPayloadBodiesByHashV1": { + func(t *testing.T) { testGetPayloadBodiesByHashV1(t, chain, client) }, + }, + "GetPayloadBodiesByRangeV1": { + func(t *testing.T) { testGetPayloadBodiesByRangeV1(t, chain, client) }, + }, + "NewPayloadV1": { + func(t *testing.T) { testNewPayloadV1(t, chain, client) }, + }, + "NewPayloadV2": { + func(t *testing.T) { testNewPayloadV2(t, chain, client) }, + }, + "NewPayloadV3": { + func(t *testing.T) { testNewPayloadV3(t, chain, client) }, + }, + "ForkchoiceUpdatedV1": { + func(t *testing.T) { testForkchoiceUpdatedV1(t, chain, client) }, + }, + "ForkchoiceUpdatedV2": { + func(t *testing.T) { testForkchoiceUpdatedV2(t, chain, client) }, + }, + "ForkchoiceUpdatedV3": { + func(t *testing.T) { testForkchoiceUpdatedV3(t, chain, client) }, + }, + "GetPayloadV3": { + func(t *testing.T) { testGetPayloadV3(t, chain, client) }, + }, + "GetPayloadV2": { + func(t *testing.T) { testGetPayloadV2(t, chain, client) }, + }, + "GetPayloadV1": { + func(t *testing.T) { testGetPayloadV1(t, chain, client) }, + }, + } + + t.Parallel() + for name, tt := range tests { + t.Run(name, tt.test) + } +} +func testExchangeCapabilities(t *testing.T, chain []*types.Block, client *Client) { + expected := catalyst.Caps + capabilities := []string{"random", "ignored", "strings"} + actual, err := client.ExchangeCapabilities(context.Background(), capabilities) + if err != nil { + t.Fatalf("ExchangeCapabilitiesV1 failed: %v", err) + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Expected capabilities %v, got %v", expected, actual) + } +} + +func testGetClientV1(t *testing.T, chain []*types.Block, client *Client) { + actual, err := client.GetClientVersionV1(context.Background()) + if err != nil { + t.Fatalf("GetClientVersionV1 failed: %v", err) + } + if !strings.Contains(fmt.Sprint(actual), "go-ethereum") { + t.Fatalf("Expected go-ethereum client version, got %v", actual) + } +} + +func testGetPayloadBodiesByHashV1(t *testing.T, chain []*types.Block, client *Client) { + actual, err := client.GetPayloadBodiesByHashV1(context.Background(), []common.Hash{chain[2].Hash()}) + if err != nil { + t.Fatalf("GetPayloadBodiesByHashV1 failed: %v", err) + } + if len(actual) != 1 { + t.Fatalf("Expected 1 payload body, got %v", actual) + } + + if actual[0].TransactionData == nil { + t.Fatalf("Expected payload body, got %v", actual[0]) + } + + tx := &types.Transaction{} + if err := tx.UnmarshalBinary(actual[0].TransactionData[0]); err != nil { + t.Fatalf("Failed to unmarshal transaction: %v", err) + } + if tx.Hash() != testTx1.Hash() { + t.Fatalf("Expected transaction %v, got %v", testTx1, tx) + } +} + +func testGetPayloadBodiesByRangeV1(t *testing.T, chain []*types.Block, client *Client) { + actual, err := client.GetPayloadBodiesByRangeV1(context.Background(), hexutil.Uint64(chain[2].NumberU64()), hexutil.Uint64(1)) + if err != nil { + t.Fatalf("GetPayloadBodiesByRangeV1 failed: %v", err) + } + if len(actual) != 1 { + t.Fatalf("Expected 1 payload body, got %v", len(actual)) + } + + if actual[0].TransactionData == nil { + t.Fatalf("Expected payload body, got %v", actual[0]) + } + + tx := &types.Transaction{} + fmt.Println(actual[0].TransactionData) + tx.UnmarshalBinary(actual[0].TransactionData[0]) + if tx.Hash() != testTx1.Hash() { + t.Fatalf("Expected transaction %v, got %v", testTx1, tx) + } +} + +func testNewPayloadV1(t *testing.T, chain []*types.Block, client *Client) { + ctx := context.Background() + + // Create a mock payload + payload := createMockPayload(chain[len(chain)-1]) + + // Call NewPayloadV1 + status, err := client.NewPayloadV1(ctx, payload) + if err != nil { + t.Fatalf("NewPayloadV1 failed: %v", err) + } + if status.Status != engine.INVALID { + t.Fatalf("Expected payload status to be INVALID, got %v", status.Status) + } +} + +func testNewPayloadV2(t *testing.T, chain []*types.Block, client *Client) { + ctx := context.Background() + + // Create a mock payload + payload := createMockPayload(chain[len(chain)-1]) + + // Call NewPayloadV1 + status, err := client.NewPayloadV1(ctx, payload) + if err != nil { + t.Fatalf("NewPayloadV1 failed: %v", err) + } + if status.Status != engine.INVALID { + t.Fatalf("Expected payload status to be INVALID, got %v", status.Status) + } +} + +func testNewPayloadV3(t *testing.T, chain []*types.Block, client *Client) { + ctx := context.Background() + + // Create a mock payload + payload := createMockPayload(chain[len(chain)-1]) + + // Call NewPayloadV1 + status, err := client.NewPayloadV1(ctx, payload) + if err != nil { + t.Fatalf("NewPayloadV1 failed: %v", err) + } + if status.Status != engine.INVALID { + t.Fatalf("Expected payload status to be INVALID, got %v", status.Status) + } +} + +func createMockPayload(parent *types.Block) *engine.ExecutionPayloadEnvelope { + // Assuming createMockPayload creates and returns a mock ExecutionPayloadEnvelope + // This is a placeholder for actual payload creation code + return &engine.ExecutionPayloadEnvelope{ + ExecutionPayload: &engine.ExecutableData{ + ParentHash: parent.Hash(), + BlockHash: common.BytesToHash(crypto.Keccak256([]byte("randomBlockHash"))), + FeeRecipient: common.BytesToAddress(crypto.Keccak256([]byte("randomFeeRecipient"))), + StateRoot: common.BytesToHash(crypto.Keccak256([]byte("randomStateRoot"))), + ReceiptsRoot: common.BytesToHash(crypto.Keccak256([]byte("randomReceiptsRoot"))), + LogsBloom: crypto.Keccak256([]byte("randomLogsBloom")), + Random: common.BytesToHash(crypto.Keccak256([]byte("random"))), + Number: parent.NumberU64() + 1, + GasLimit: 21000, + GasUsed: 10500, + Timestamp: 1630425600, + ExtraData: []byte("randomExtraData"), + BaseFeePerGas: big.NewInt(1000000000), + Transactions: [][]byte{ + crypto.Keccak256([]byte("randomTransaction1")), + crypto.Keccak256([]byte("randomTransaction2"))}, + }} +} + +func testForkchoiceUpdatedV1(t *testing.T, chain []*types.Block, client *Client) { + // Call ForkchoiceUpdatedV2 + resp, err := client.ForkchoiceUpdatedV1(context.Background(), &engine.ForkchoiceStateV1{ + HeadBlockHash: common.Hash{}, + }, nil) + if err != nil { + t.Fatalf("ForkchoiceUpdatedV2 failed: %v", err) + } + if resp.Status == nil { + t.Fatalf("Expected status, got %v", resp.Status) + } + + if resp.Status.Status != engine.INVALID { + t.Fatalf("Expected status to be INVALID, got %v", resp.Status.Status) + } +} + +func testForkchoiceUpdatedV2(t *testing.T, chain []*types.Block, client *Client) { + // Call ForkchoiceUpdatedV2 + resp, err := client.ForkchoiceUpdatedV2(context.Background(), &engine.ForkchoiceStateV1{ + HeadBlockHash: common.Hash{}, + }, nil) + if err != nil { + t.Fatalf("ForkchoiceUpdatedV2 failed: %v", err) + } + if resp.Status == nil { + t.Fatalf("Expected status, got %v", resp.Status) + } + + if resp.Status.Status != engine.INVALID { + t.Fatalf("Expected status to be INVALID, got %v", resp.Status.Status) + } +} + +func testForkchoiceUpdatedV3(t *testing.T, chain []*types.Block, client *Client) { + // Call ForkchoiceUpdatedV3 + resp, err := client.ForkchoiceUpdatedV3(context.Background(), &engine.ForkchoiceStateV1{ + HeadBlockHash: common.Hash{}, + }, nil) + if err != nil { + t.Fatalf("ForkchoiceUpdatedV3 failed: %v", err) + } + if resp.Status == nil { + t.Fatalf("Expected status, got %v", resp.Status) + } + + if resp.Status.Status != engine.INVALID { + t.Fatalf("Expected status to be INVALID, got %v", resp.Status.Status) + } +} + +func testGetPayloadV3(t *testing.T, chain []*types.Block, client *Client) { + payloadID := engine.PayloadID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} // Example PayloadID, adjust as necessary + _, err := client.GetPayloadV3(context.Background(), &payloadID) + if err.Error() != "Unsupported fork" { + t.Fatalf("GetPayloadV3 failed: %v", err) + } +} + +func testGetPayloadV2(t *testing.T, chain []*types.Block, client *Client) { + payloadID := engine.PayloadID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} // Example PayloadID, adjust as necessary + _, err := client.GetPayloadV2(context.Background(), &payloadID) + if err.Error() != "Unknown payload" { + t.Fatalf("Expected unknown payload error, got: %v", err) + } +} + +func testGetPayloadV1(t *testing.T, chain []*types.Block, client *Client) { + payloadID := engine.PayloadID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} // Example PayloadID, adjust as necessary + _, err := client.GetPayloadV1(context.Background(), &payloadID) + if err.Error() != "Unknown payload" { + t.Fatalf("Expected unknown payload error, got: %v", err) + } +} diff --git a/ethclient/engineclient/types.go b/ethclient/engineclient/types.go new file mode 100644 index 0000000000..60ce50addc --- /dev/null +++ b/ethclient/engineclient/types.go @@ -0,0 +1,23 @@ +package engineclient + +import ( + "github.com/ethereum/go-ethereum/beacon/engine" +) + +// APIVersion is a custom type for the engine API version. +type APIVersion int + +const ( + Phase0 APIVersion = iota + ParisV1 + ShanghaiV2 + CancunV3 +) + +// ForkchoiceUpdatedResponse is the response kind received by the +// engine_forkchoiceUpdatedV1 endpoint. +type ForkchoiceUpdatedResponse struct { + Status *engine.PayloadStatusV1 `json:"payloadStatus"` + PayloadId *engine.PayloadID `json:"payloadId"` + ValidationError string `json:"validationError"` +} diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 484d015e74..5c3cb79dd6 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -28,9 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - - // Temporary to get engineclient to compile for testing. Will remove - _ "github.com/ethereum/go-ethereum/ethclient/engineclient" "github.com/ethereum/go-ethereum/rpc" ) diff --git a/signer/fourbyte/4byte.json b/signer/fourbyte/4byte.json index 00f2658fbd..89046c1b9c 100644 --- a/signer/fourbyte/4byte.json +++ b/signer/fourbyte/4byte.json @@ -43029,7 +43029,7 @@ "29272a63": "BBP()", "29274fe1": "buyBOTx(uint256,string,string,address,uint256)", "2927989f": "bet(bool,address,uint256)", -"29287ad0": "releaseTheRest()", +"29287ad0": "releToEtherest()", "29287edb": "calRebase()", "2928859c": "runsOutOfGas()", "29291054": "setContract(address,address,address)", @@ -82005,7 +82005,7 @@ "4e06c968": "changeDragonDen()", "4e07008d": "mintWithEther(address,uint256)", "4e070f50": "tokenDecaySupplyForWeek(uint256)", -"4e077f2a": "addGasEther()", +"4e077f2a": "addGToEther()", "4e07a7a5": "thirdStageMinted()", "4e083278": "depositPrize()", "4e0853db": "_setVAIVaultInfo(address,uint256,uint256)",