diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 4bd0e2490a..3b21855c8d 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -219,15 +219,16 @@ var TestBatchPosterConfig = BatchPosterConfig{ } type BatchPosterOpts struct { - DataPosterDB ethdb.Database - L1Reader *headerreader.HeaderReader - Inbox *InboxTracker - Streamer *TransactionStreamer - SyncMonitor *SyncMonitor - Config BatchPosterConfigFetcher - DeployInfo *chaininfo.RollupAddresses - TransactOpts *bind.TransactOpts - DAWriter das.DataAvailabilityServiceWriter + DataPosterDB ethdb.Database + L1Reader *headerreader.HeaderReader + Inbox *InboxTracker + Streamer *TransactionStreamer + SyncMonitor *SyncMonitor + Config BatchPosterConfigFetcher + DeployInfo *chaininfo.RollupAddresses + TransactOpts *bind.TransactOpts + DAWriter das.DataAvailabilityServiceWriter + ParentChainID *big.Int } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -291,6 +292,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e MetadataRetriever: b.getBatchPosterPosition, ExtraBacklog: b.GetBacklogEstimate, RedisKey: "data-poster.queue", + ParentChainID: opts.ParentChainID, }) if err != nil { return nil, err diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index ff5a945a17..09f3e218b1 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/go-redis/redis/v8" "github.com/offchainlabs/nitro/arbnode/dataposter/dbstorage" "github.com/offchainlabs/nitro/arbnode/dataposter/noop" @@ -62,6 +63,7 @@ type DataPoster struct { replacementTimes []time.Duration metadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) extraBacklog func() uint64 + parentChainID *big.Int // These fields are protected by the mutex. // TODO: factor out these fields into separate structure, since now one @@ -113,6 +115,7 @@ type DataPosterOpts struct { MetadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) ExtraBacklog func() uint64 RedisKey string // Redis storage key + ParentChainID *big.Int } func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, error) { @@ -172,6 +175,7 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro errorCount: make(map[uint64]int), maxFeeCapExpression: expression, extraBacklog: opts.ExtraBacklog, + parentChainID: opts.ParentChainID, } if dp.extraBacklog == nil { dp.extraBacklog = func() uint64 { return 0 } @@ -189,6 +193,7 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro }, } } + return dp, nil } @@ -229,6 +234,35 @@ func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error ) } +// txToSendTxArgs converts transaction to SendTxArgs. This is needed for +// external signer to specify From field. +func txToSendTxArgs(addr common.Address, tx *types.Transaction) (*apitypes.SendTxArgs, error) { + var to *common.MixedcaseAddress + if tx.To() != nil { + to = new(common.MixedcaseAddress) + *to = common.NewMixedcaseAddress(*tx.To()) + } + data := (hexutil.Bytes)(tx.Data()) + val := (*hexutil.Big)(tx.Value()) + if val == nil { + val = (*hexutil.Big)(big.NewInt(0)) + } + al := tx.AccessList() + return &apitypes.SendTxArgs{ + From: common.NewMixedcaseAddress(addr), + To: to, + Gas: hexutil.Uint64(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()), + MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()), + Value: *val, + Nonce: hexutil.Uint64(tx.Nonce()), + Data: &data, + AccessList: &al, + ChainID: (*hexutil.Big)(tx.ChainId()), + }, nil +} + // externalSigner returns signer function and ethereum address of the signer. // Returns an error if address isn't specified or if it can't connect to the // signer RPC server. @@ -242,27 +276,27 @@ func externalSigner(ctx context.Context, opts *ExternalSignerCfg) (signerFn, com return nil, common.Address{}, fmt.Errorf("error connecting external signer: %w", err) } sender := common.HexToAddress(opts.Address) - - var hasher types.Signer return func(ctx context.Context, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { // According to the "eth_signTransaction" API definition, this should be // RLP encoded transaction object. // https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction var data hexutil.Bytes - if err := client.CallContext(ctx, &data, opts.Method, tx); err != nil { - return nil, fmt.Errorf("signing transaction: %w", err) + args, err := txToSendTxArgs(addr, tx) + if err != nil { + return nil, fmt.Errorf("error converting transaction to sendTxArgs: %w", err) } - var signedTx types.Transaction - if err := rlp.DecodeBytes(data, &signedTx); err != nil { - return nil, fmt.Errorf("error decoding signed transaction: %w", err) + if err := client.CallContext(ctx, &data, opts.Method, args); err != nil { + return nil, fmt.Errorf("making signing request to external signer: %w", err) } - if hasher == nil { - hasher = types.LatestSignerForChainID(tx.ChainId()) + signedTx := &types.Transaction{} + if err := signedTx.UnmarshalBinary(data); err != nil { + return nil, fmt.Errorf("unmarshaling signed transaction: %w", err) } - if hasher.Hash(tx) != hasher.Hash(&signedTx) { - return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(&signedTx), hasher.Hash(tx)) + hasher := types.LatestSignerForChainID(tx.ChainId()) + if h := hasher.Hash(args.ToTransaction()); h != hasher.Hash(signedTx) { + return nil, fmt.Errorf("transaction: %x from external signer differs from request: %x", hasher.Hash(signedTx), h) } - return &signedTx, nil + return signedTx, nil }, sender, nil } @@ -549,6 +583,7 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim Value: value, Data: calldata, AccessList: accessList, + ChainID: p.parentChainID, } fullTx, err := p.signer(ctx, p.Sender(), types.NewTx(&inner)) if err != nil { diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index 74b4aff18e..3d7fa60dc7 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -2,27 +2,18 @@ package dataposter import ( "context" - "crypto/tls" - "crypto/x509" - "encoding/json" "fmt" - "io" "math/big" "net/http" - "os" "testing" "time" "github.com/Knetic/govaluate" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/google/go-cmp/cmp" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" ) func TestParseReplacementTimes(t *testing.T) { @@ -60,9 +51,24 @@ func TestParseReplacementTimes(t *testing.T) { } } +func signerTestCfg(addr common.Address) (*ExternalSignerCfg, error) { + cp, err := externalsignertest.CertPaths() + if err != nil { + return nil, fmt.Errorf("getting certificates path: %w", err) + } + return &ExternalSignerCfg{ + Address: common.Bytes2Hex(addr.Bytes()), + URL: externalsignertest.SignerURL, + Method: externalsignertest.SignerMethod, + RootCA: cp.ServerCert, + ClientCert: cp.ClientCert, + ClientPrivateKey: cp.ClientKey, + }, nil +} + func TestExternalSigner(t *testing.T) { ctx := context.Background() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -76,172 +82,39 @@ func TestExternalSigner(t *testing.T) { return } }() - signer, addr, err := externalSigner(ctx, - &ExternalSignerCfg{ - Address: srv.address.Hex(), - URL: "https://localhost:1234", - Method: "test_signTransaction", - RootCA: cert, - ClientCert: "./testdata/client.crt", - ClientPrivateKey: "./testdata/client.key", - }) - if err != nil { - t.Fatalf("Error getting external signer: %v", err) - } - tx := types.NewTransaction(13, common.HexToAddress("0x01"), big.NewInt(1), 2, big.NewInt(3), []byte{0x01, 0x02, 0x03}) - got, err := signer(ctx, addr, tx) - if err != nil { - t.Fatalf("Error signing transaction with external signer: %v", err) - } - want, err := srv.signerFn(addr, tx) - if err != nil { - t.Fatalf("Error signing transaction: %v", err) - } - if diff := cmp.Diff(want.Hash(), got.Hash()); diff != "" { - t.Errorf("Signing transaction: unexpected diff: %v\n", diff) - } -} - -type server struct { - handlers map[string]func(*json.RawMessage) (string, error) - signerFn bind.SignerFn - address common.Address -} - -type request struct { - ID *json.RawMessage `json:"id"` - Method string `json:"method"` - Params *json.RawMessage `json:"params"` -} - -type response struct { - ID *json.RawMessage `json:"id"` - Result string `json:"result,omitempty"` -} - -// newServer returns http server and server struct that implements RPC methods. -// It sets up an account in temporary directory and cleans up after test is -// done. -func newServer(ctx context.Context, t *testing.T) (*http.Server, *server) { - t.Helper() - signer, address, err := setupAccount("/tmp/keystore") + signerCfg, err := signerTestCfg(srv.Address) if err != nil { - t.Fatalf("Error setting up account: %v", err) + t.Fatalf("Error getting signer test config: %v", err) } - t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) - - s := &server{signerFn: signer, address: address} - s.handlers = map[string]func(*json.RawMessage) (string, error){ - "test_signTransaction": s.signTransaction, - } - m := http.NewServeMux() - - clientCert, err := os.ReadFile("./testdata/client.crt") + signer, addr, err := externalSigner(ctx, signerCfg) if err != nil { - t.Fatalf("Error reading client certificate: %v", err) + t.Fatalf("Error getting external signer: %v", err) } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(clientCert) - - httpSrv := &http.Server{ - Addr: ":1234", - Handler: m, - ReadTimeout: 5 * time.Second, - TLSConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: pool, + tx := types.NewTx( + &types.DynamicFeeTx{ + Nonce: 13, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(1), + Gas: 3, + To: nil, + Value: big.NewInt(1), + Data: []byte{0x01, 0x02, 0x03}, }, - } - m.HandleFunc("/", s.mux) - return httpSrv, s -} - -// setupAccount creates a new account in a given directory, unlocks it, creates -// signer with that account and returns it along with account address. -func setupAccount(dir string) (bind.SignerFn, common.Address, error) { - ks := keystore.NewKeyStore( - dir, - keystore.StandardScryptN, - keystore.StandardScryptP, ) - a, err := ks.NewAccount("password") - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) - } - if err := ks.Unlock(a, "password"); err != nil { - return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) - } - txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1)) - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) - } - return txOpts.Signer, a.Address, nil -} - -// UnmarshallFirst unmarshalls slice of params and returns the first one. -// Parameters in Go ethereum RPC calls are marashalled as slices. E.g. -// eth_sendRawTransaction or eth_signTransaction, marshall transaction as a -// slice of transactions in a message: -// https://github.com/ethereum/go-ethereum/blob/0004c6b229b787281760b14fb9460ffd9c2496f1/rpc/client.go#L548 -func unmarshallFirst(params []byte) (*types.Transaction, error) { - var arr []apitypes.SendTxArgs - if err := json.Unmarshal(params, &arr); err != nil { - return nil, fmt.Errorf("unmarshaling first param: %w", err) - } - if len(arr) != 1 { - return nil, fmt.Errorf("argument should be a single transaction, but got: %d", len(arr)) - } - return arr[0].ToTransaction(), nil -} - -func (s *server) signTransaction(params *json.RawMessage) (string, error) { - tx, err := unmarshallFirst(*params) - if err != nil { - return "", err - } - signedTx, err := s.signerFn(s.address, tx) - if err != nil { - return "", fmt.Errorf("signing transaction: %w", err) - } - data, err := rlp.EncodeToBytes(signedTx) - if err != nil { - return "", fmt.Errorf("rlp encoding transaction: %w", err) - } - return hexutil.Encode(data), nil -} - -func (s *server) mux(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) + got, err := signer(ctx, addr, tx) if err != nil { - http.Error(w, "can't read body", http.StatusBadRequest) - return - } - var req request - if err := json.Unmarshal(body, &req); err != nil { - http.Error(w, "can't unmarshal JSON request", http.StatusBadRequest) - return - } - method, ok := s.handlers[req.Method] - if !ok { - http.Error(w, "method not found", http.StatusNotFound) - return + t.Fatalf("Error signing transaction with external signer: %v", err) } - result, err := method(req.Params) + args, err := txToSendTxArgs(addr, tx) if err != nil { - fmt.Printf("error calling method: %v\n", err) - http.Error(w, "error calling method", http.StatusInternalServerError) - return + t.Fatalf("Error converting transaction to sendTxArgs: %v", err) } - resp := response{ID: req.ID, Result: result} - respBytes, err := json.Marshal(resp) + want, err := srv.SignerFn(addr, args.ToTransaction()) if err != nil { - http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) - return + t.Fatalf("Error signing transaction: %v", err) } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(respBytes); err != nil { - fmt.Printf("error writing response: %v\n", err) + if diff := cmp.Diff(want.Hash(), got.Hash()); diff != "" { + t.Errorf("Signing transaction: unexpected diff: %v\n", diff) } } diff --git a/arbnode/dataposter/externalsignertest/externalsignertest.go b/arbnode/dataposter/externalsignertest/externalsignertest.go new file mode 100644 index 0000000000..7d15515feb --- /dev/null +++ b/arbnode/dataposter/externalsignertest/externalsignertest.go @@ -0,0 +1,153 @@ +package externalsignertest + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "math/big" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +var ( + dataPosterPath = "arbnode/dataposter" + selfPath = filepath.Join(dataPosterPath, "externalsignertest") + + SignerPort = 1234 + SignerURL = fmt.Sprintf("https://localhost:%v", SignerPort) + SignerMethod = "test_signTransaction" +) + +type CertAbsPaths struct { + ServerCert string + ServerKey string + ClientCert string + ClientKey string +} + +func basePath() (string, error) { + _, file, _, ok := runtime.Caller(1) + if !ok { + return "", fmt.Errorf("error getting caller") + } + idx := strings.Index(file, selfPath) + if idx == -1 { + return "", fmt.Errorf("error determining base path, selfPath: %q is not substring of current file path: %q", selfPath, file) + } + return file[:idx], nil +} + +func testDataPath() (string, error) { + base, err := basePath() + if err != nil { + return "", fmt.Errorf("getting base path: %w", err) + } + return filepath.Join(base, dataPosterPath, "testdata"), nil +} + +func CertPaths() (*CertAbsPaths, error) { + td, err := testDataPath() + if err != nil { + return nil, fmt.Errorf("getting test data path: %w", err) + } + return &CertAbsPaths{ + ServerCert: filepath.Join(td, "localhost.crt"), + ServerKey: filepath.Join(td, "localhost.key"), + ClientCert: filepath.Join(td, "client.crt"), + ClientKey: filepath.Join(td, "client.key"), + }, nil +} + +func NewServer(ctx context.Context, t *testing.T) (*http.Server, *SignerAPI) { + rpcServer := rpc.NewServer() + signer, address, err := setupAccount("/tmp/keystore") + if err != nil { + t.Fatalf("Error setting up account: %v", err) + } + t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) + s := &SignerAPI{SignerFn: signer, Address: address} + if err := rpcServer.RegisterName("test", s); err != nil { + t.Fatalf("Failed to register EthSigningAPI, error: %v", err) + } + cp, err := CertPaths() + if err != nil { + t.Fatalf("Error getting certificate paths: %v", err) + } + clientCert, err := os.ReadFile(cp.ClientCert) + if err != nil { + t.Fatalf("Error reading client certificate: %v", err) + } + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(clientCert) + + httpServer := &http.Server{ + Addr: fmt.Sprintf(":%d", SignerPort), + Handler: rpcServer, + ReadTimeout: 30 * time.Second, + ReadHeaderTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: pool, + }, + } + + return httpServer, s +} + +// setupAccount creates a new account in a given directory, unlocks it, creates +// signer with that account and returns it along with account address. +func setupAccount(dir string) (bind.SignerFn, common.Address, error) { + ks := keystore.NewKeyStore( + dir, + keystore.StandardScryptN, + keystore.StandardScryptP, + ) + a, err := ks.NewAccount("password") + if err != nil { + return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) + } + if err := ks.Unlock(a, "password"); err != nil { + return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) + } + txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1337)) + if err != nil { + return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) + } + return txOpts.Signer, a.Address, nil +} + +type SignerAPI struct { + SignerFn bind.SignerFn + Address common.Address +} + +func (a *SignerAPI) SignTransaction(ctx context.Context, req *apitypes.SendTxArgs) (hexutil.Bytes, error) { + if req == nil { + return nil, fmt.Errorf("nil request") + } + signedTx, err := a.SignerFn(a.Address, req.ToTransaction()) + if err != nil { + return nil, fmt.Errorf("signing transaction: %w", err) + } + signedTxBytes, err := signedTx.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("marshaling signed transaction: %w", err) + } + return signedTxBytes, nil +} diff --git a/arbnode/node.go b/arbnode/node.go index eaefb8ecbe..f92dcefe7c 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -300,6 +300,7 @@ func checkArbDbSchemaVersion(arbDb ethdb.Database) error { func StakerDataposter( ctx context.Context, db ethdb.Database, l1Reader *headerreader.HeaderReader, transactOpts *bind.TransactOpts, cfgFetcher ConfigFetcher, syncMonitor *SyncMonitor, + parentChainID *big.Int, ) (*dataposter.DataPoster, error) { cfg := cfgFetcher.Get() if transactOpts == nil && cfg.Staker.DataPoster.ExternalSigner.URL == "" { @@ -330,6 +331,7 @@ func StakerDataposter( Config: dpCfg, MetadataRetriever: mdRetriever, RedisKey: sender + ".staker-data-poster.queue", + ParentChainID: parentChainID, }) } @@ -346,6 +348,7 @@ func createNodeImpl( txOptsBatchPoster *bind.TransactOpts, dataSigner signature.DataSignerFunc, fatalErrChan chan error, + parentChainID *big.Int, ) (*Node, error) { config := configFetcher.Get() @@ -567,6 +570,7 @@ func createNodeImpl( txOptsValidator, configFetcher, syncMonitor, + parentChainID, ) if err != nil { return nil, err @@ -634,15 +638,16 @@ func createNodeImpl( return nil, errors.New("batchposter, but no TxOpts") } batchPoster, err = NewBatchPoster(ctx, &BatchPosterOpts{ - DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), - L1Reader: l1Reader, - Inbox: inboxTracker, - Streamer: txStreamer, - SyncMonitor: syncMonitor, - Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, - DeployInfo: deployInfo, - TransactOpts: txOptsBatchPoster, - DAWriter: daWriter, + DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), + L1Reader: l1Reader, + Inbox: inboxTracker, + Streamer: txStreamer, + SyncMonitor: syncMonitor, + Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, + DeployInfo: deployInfo, + TransactOpts: txOptsBatchPoster, + DAWriter: daWriter, + ParentChainID: parentChainID, }) if err != nil { return nil, err @@ -700,8 +705,9 @@ func CreateNode( txOptsBatchPoster *bind.TransactOpts, dataSigner signature.DataSignerFunc, fatalErrChan chan error, + parentChainID *big.Int, ) (*Node, error) { - currentNode, err := createNodeImpl(ctx, stack, exec, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan) + currentNode, err := createNodeImpl(ctx, stack, exec, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan, parentChainID) if err != nil { return nil, err } diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index 12d494a230..b66710dbf0 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -24,6 +24,7 @@ type L1Interface interface { TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) BlockNumber(ctx context.Context) (uint64, error) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) + ChainID(ctx context.Context) (*big.Int, error) } func SendTxAsCall(ctx context.Context, client L1Interface, tx *types.Transaction, from common.Address, blockNum *big.Int, unlimitedGas bool) ([]byte, error) { diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 55c8d7704a..45f539488d 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -536,6 +536,7 @@ func mainImpl() int { l1TransactionOptsBatchPoster, dataSigner, fatalErrChan, + big.NewInt(int64(nodeConfig.ParentChain.ID)), ) if err != nil { log.Error("failed to create node", "err", err) diff --git a/system_tests/batch_poster_test.go b/system_tests/batch_poster_test.go index 873a87c6f9..f7bf74f699 100644 --- a/system_tests/batch_poster_test.go +++ b/system_tests/batch_poster_test.go @@ -20,6 +20,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/dataposter" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/util/redisutil" @@ -60,10 +62,29 @@ func addNewBatchPoster(ctx context.Context, t *testing.T, builder *NodeBuilder, } } +func externalSignerTestCfg(addr common.Address) (*dataposter.ExternalSignerCfg, error) { + cp, err := externalsignertest.CertPaths() + if err != nil { + return nil, fmt.Errorf("getting certificates path: %w", err) + } + return &dataposter.ExternalSignerCfg{ + Address: common.Bytes2Hex(addr.Bytes()), + URL: externalsignertest.SignerURL, + Method: externalsignertest.SignerMethod, + RootCA: cp.ServerCert, + ClientCert: cp.ClientCert, + ClientPrivateKey: cp.ClientKey, + }, nil +} + func testBatchPosterParallel(t *testing.T, useRedis bool) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) + cp, err := externalsignertest.CertPaths() + if err != nil { + t.Fatalf("Error getting cert paths: %v", err) + } t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -71,7 +92,7 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { }) go func() { log.Debug("Server is listening on port 1234...") - if err := httpSrv.ListenAndServeTLS(signerServerCert, signerServerKey); err != nil && err != http.ErrServerClosed { + if err := httpSrv.ListenAndServeTLS(cp.ServerCert, cp.ServerKey); err != nil && err != http.ErrServerClosed { log.Debug("ListenAndServeTLS() failed", "error", err) return } @@ -93,7 +114,11 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { builder := NewNodeBuilder(ctx).DefaultConfig(t, true) builder.nodeConfig.BatchPoster.Enable = false builder.nodeConfig.BatchPoster.RedisUrl = redisUrl - builder.nodeConfig.BatchPoster.DataPoster.ExternalSigner = *externalSignerTestCfg(srv.address) + signerCfg, err := externalSignerTestCfg(srv.Address) + if err != nil { + t.Fatalf("Error getting external signer config: %v", err) + } + builder.nodeConfig.BatchPoster.DataPoster.ExternalSigner = *signerCfg cleanup := builder.Build(t) defer cleanup() @@ -101,10 +126,10 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { defer cleanupB() builder.L2Info.GenerateAccount("User2") - addNewBatchPoster(ctx, t, builder, srv.address) + addNewBatchPoster(ctx, t, builder, srv.Address) builder.L1.SendWaitTestTransactions(t, []*types.Transaction{ - builder.L1Info.PrepareTxTo("Faucet", &srv.address, 30000, big.NewInt(1e18), nil)}) + builder.L1Info.PrepareTxTo("Faucet", &srv.Address, 30000, big.NewInt(1e18), nil)}) var txs []*types.Transaction @@ -128,20 +153,25 @@ func testBatchPosterParallel(t *testing.T, useRedis bool) { builder.nodeConfig.BatchPoster.MaxSize = len(firstTxData) * 2 startL1Block, err := builder.L1.Client.BlockNumber(ctx) Require(t, err) + parentChainID, err := builder.L1.Client.ChainID(ctx) + if err != nil { + t.Fatalf("Failed to get parent chain id: %v", err) + } for i := 0; i < parallelBatchPosters; i++ { // Make a copy of the batch poster config so NewBatchPoster calling Validate() on it doesn't race batchPosterConfig := builder.nodeConfig.BatchPoster batchPoster, err := arbnode.NewBatchPoster(ctx, &arbnode.BatchPosterOpts{ - DataPosterDB: nil, - L1Reader: builder.L2.ConsensusNode.L1Reader, - Inbox: builder.L2.ConsensusNode.InboxTracker, - Streamer: builder.L2.ConsensusNode.TxStreamer, - SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, - Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, - DeployInfo: builder.L2.ConsensusNode.DeployInfo, - TransactOpts: &seqTxOpts, - DAWriter: nil, + DataPosterDB: nil, + L1Reader: builder.L2.ConsensusNode.L1Reader, + Inbox: builder.L2.ConsensusNode.InboxTracker, + Streamer: builder.L2.ConsensusNode.TxStreamer, + SyncMonitor: builder.L2.ConsensusNode.SyncMonitor, + Config: func() *arbnode.BatchPosterConfig { return &batchPosterConfig }, + DeployInfo: builder.L2.ConsensusNode.DeployInfo, + TransactOpts: &seqTxOpts, + DAWriter: nil, + ParentChainID: parentChainID, }, ) Require(t, err) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 937b8980fc..2e17a50ede 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -782,10 +782,9 @@ func createTestNodeWithL1( execConfigFetcher := func() *gethexec.Config { return execConfig } execNode, err := gethexec.CreateExecutionNode(ctx, l2stack, l2chainDb, l2blockchain, l1client, execConfigFetcher) Require(t, err) - currentNode, err = arbnode.CreateNode( ctx, l2stack, execNode, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, - addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, fatalErrChan, + addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, fatalErrChan, big.NewInt(1337), ) Require(t, err) @@ -821,7 +820,7 @@ func createTestNode( execNode, err := gethexec.CreateExecutionNode(ctx, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - currentNode, err := arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(nodeConfig), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan) + currentNode, err := arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(nodeConfig), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan, big.NewInt(1337)) Require(t, err) // Give the node an init message @@ -925,7 +924,7 @@ func Create2ndNodeWithConfig( currentExec, err := gethexec.CreateExecutionNode(ctx, l2stack, l2chainDb, l2blockchain, l1client, configFetcher) Require(t, err) - currentNode, err := arbnode.CreateNode(ctx, l2stack, currentExec, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, first.DeployInfo, &txOpts, &txOpts, dataSigner, feedErrChan) + currentNode, err := arbnode.CreateNode(ctx, l2stack, currentExec, l2arbDb, NewFetcherFromConfig(nodeConfig), l2blockchain.Config(), l1client, first.DeployInfo, &txOpts, &txOpts, dataSigner, feedErrChan, big.NewInt(13)) Require(t, err) err = currentNode.Start(ctx) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index 6db339521c..96de52e197 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -123,7 +123,7 @@ func TestDASRekey(t *testing.T) { l1NodeConfigB := arbnode.ConfigDefaultL1NonSequencerTest() sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) sequencerTxOptsPtr := &sequencerTxOpts - + parentChainID := big.NewInt(1337) { authorizeDASKeyset(t, ctx, pubkeyA, l1info, l1client) @@ -141,8 +141,7 @@ func TestDASRekey(t *testing.T) { l1NodeConfigA.DataAvailability.ParentChainNodeURL = "none" execA, err := gethexec.CreateExecutionNode(ctx, l2stackA, l2chainDb, l2blockchain, l1client, gethexec.ConfigDefaultTest) Require(t, err) - - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan, parentChainID) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) @@ -189,7 +188,7 @@ func TestDASRekey(t *testing.T) { Require(t, err) l1NodeConfigA.DataAvailability.RPCAggregator = aggConfigForBackend(t, backendConfigB) - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, nil, feedErrChan, parentChainID) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) @@ -322,7 +321,7 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) sequencerTxOptsPtr := &sequencerTxOpts - nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, feedErrChan) + nodeA, err := arbnode.CreateNode(ctx, l2stackA, execA, l2arbDb, NewFetcherFromConfig(l1NodeConfigA), l2blockchain.Config(), l1client, addresses, sequencerTxOptsPtr, sequencerTxOptsPtr, dataSigner, feedErrChan, big.NewInt(1337)) Require(t, err) Require(t, nodeA.Start(ctx)) l2clientA := ClientForStack(t, l2stackA) diff --git a/system_tests/external_signer.go b/system_tests/external_signer.go deleted file mode 100644 index 1ee9c85581..0000000000 --- a/system_tests/external_signer.go +++ /dev/null @@ -1,188 +0,0 @@ -package arbtest - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "os" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/offchainlabs/nitro/arbnode/dataposter" -) - -var ( - signerPort = 1234 - signerURL = fmt.Sprintf("https://localhost:%v", signerPort) - signerMethod = "test_signTransaction" - signerServerCert = "../arbnode/dataposter/testdata/localhost.crt" - signerServerKey = "../arbnode/dataposter/testdata/localhost.key" - signerClientCert = "../arbnode/dataposter/testdata/client.crt" - signerClientPrivateKey = "../arbnode/dataposter/testdata/client.key" -) - -func externalSignerTestCfg(addr common.Address) *dataposter.ExternalSignerCfg { - return &dataposter.ExternalSignerCfg{ - Address: common.Bytes2Hex(addr.Bytes()), - URL: signerURL, - Method: signerMethod, - RootCA: signerServerCert, - ClientCert: signerClientCert, - ClientPrivateKey: signerClientPrivateKey, - } -} - -type server struct { - handlers map[string]func(*json.RawMessage) (string, error) - signerFn bind.SignerFn - address common.Address -} - -type request struct { - ID *json.RawMessage `json:"id"` - Method string `json:"method"` - Params *json.RawMessage `json:"params"` -} - -type response struct { - ID *json.RawMessage `json:"id"` - Result string `json:"result,omitempty"` -} - -// newServer returns http server and server struct that implements RPC methods. -// It sets up an account in temporary directory and cleans up after test is -// done. -func newServer(ctx context.Context, t *testing.T) (*http.Server, *server) { - t.Helper() - signer, address, err := setupAccount("/tmp/keystore") - if err != nil { - t.Fatalf("Error setting up account: %v", err) - } - t.Cleanup(func() { os.RemoveAll("/tmp/keystore") }) - - s := &server{signerFn: signer, address: address} - s.handlers = map[string]func(*json.RawMessage) (string, error){ - signerMethod: s.signTransaction, - } - m := http.NewServeMux() - - clientCert, err := os.ReadFile(signerClientCert) - if err != nil { - t.Fatalf("Error reading client certificate: %v", err) - } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(clientCert) - - httpSrv := &http.Server{ - Addr: fmt.Sprintf(":%v", signerPort), - Handler: m, - ReadTimeout: 5 * time.Second, - TLSConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: pool, - }, - } - m.HandleFunc("/", s.mux) - return httpSrv, s -} - -// setupAccount creates a new account in a given directory, unlocks it, creates -// signer with that account and returns it along with account address. -func setupAccount(dir string) (bind.SignerFn, common.Address, error) { - ks := keystore.NewKeyStore( - dir, - keystore.StandardScryptN, - keystore.StandardScryptP, - ) - a, err := ks.NewAccount("password") - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating account account: %w", err) - } - if err := ks.Unlock(a, "password"); err != nil { - return nil, common.Address{}, fmt.Errorf("unlocking account: %w", err) - } - txOpts, err := bind.NewKeyStoreTransactorWithChainID(ks, a, big.NewInt(1337)) - if err != nil { - return nil, common.Address{}, fmt.Errorf("creating transactor: %w", err) - } - return txOpts.Signer, a.Address, nil -} - -// UnmarshallFirst unmarshalls slice of params and returns the first one. -// Parameters in Go ethereum RPC calls are marashalled as slices. E.g. -// eth_sendRawTransaction or eth_signTransaction, marshall transaction as a -// slice of transactions in a message: -// https://github.com/ethereum/go-ethereum/blob/0004c6b229b787281760b14fb9460ffd9c2496f1/rpc/client.go#L548 -func unmarshallFirst(params []byte) (*types.Transaction, error) { - var arr []apitypes.SendTxArgs - if err := json.Unmarshal(params, &arr); err != nil { - return nil, fmt.Errorf("unmarshaling first param: %w", err) - } - if len(arr) != 1 { - return nil, fmt.Errorf("argument should be a single transaction, but got: %d", len(arr)) - } - return arr[0].ToTransaction(), nil -} - -func (s *server) signTransaction(params *json.RawMessage) (string, error) { - tx, err := unmarshallFirst(*params) - if err != nil { - return "", err - } - signedTx, err := s.signerFn(s.address, tx) - if err != nil { - return "", fmt.Errorf("signing transaction: %w", err) - } - data, err := rlp.EncodeToBytes(signedTx) - if err != nil { - return "", fmt.Errorf("rlp encoding transaction: %w", err) - } - return hexutil.Encode(data), nil -} - -func (s *server) mux(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "can't read body", http.StatusBadRequest) - return - } - var req request - if err := json.Unmarshal(body, &req); err != nil { - http.Error(w, "can't unmarshal JSON request", http.StatusBadRequest) - return - } - method, ok := s.handlers[req.Method] - if !ok { - http.Error(w, "method not found", http.StatusNotFound) - return - } - result, err := method(req.Params) - if err != nil { - fmt.Printf("error calling method: %v\n", err) - http.Error(w, "error calling method", http.StatusInternalServerError) - return - } - resp := response{ID: req.ID, Result: result} - respBytes, err := json.Marshal(resp) - if err != nil { - http.Error(w, fmt.Sprintf("error encoding response: %v", err), http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(respBytes); err != nil { - fmt.Printf("error writing response: %v\n", err) - } -} diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 43ba6a056c..118d17ec81 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -278,7 +278,8 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, chall asserterRollupAddresses.SequencerInbox = asserterSeqInboxAddr asserterExec, err := gethexec.CreateExecutionNode(ctx, asserterL2Stack, asserterL2ChainDb, asserterL2Blockchain, l1Backend, gethexec.ConfigDefaultTest) Require(t, err) - asserterL2, err := arbnode.CreateNode(ctx, asserterL2Stack, asserterExec, asserterL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, asserterRollupAddresses, nil, nil, nil, fatalErrChan) + parentChainID := big.NewInt(1337) + asserterL2, err := arbnode.CreateNode(ctx, asserterL2Stack, asserterExec, asserterL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, asserterRollupAddresses, nil, nil, nil, fatalErrChan, parentChainID) Require(t, err) err = asserterL2.Start(ctx) Require(t, err) @@ -289,7 +290,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, chall challengerRollupAddresses.SequencerInbox = challengerSeqInboxAddr challengerExec, err := gethexec.CreateExecutionNode(ctx, challengerL2Stack, challengerL2ChainDb, challengerL2Blockchain, l1Backend, gethexec.ConfigDefaultTest) Require(t, err) - challengerL2, err := arbnode.CreateNode(ctx, challengerL2Stack, challengerExec, challengerL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, &challengerRollupAddresses, nil, nil, nil, fatalErrChan) + challengerL2, err := arbnode.CreateNode(ctx, challengerL2Stack, challengerExec, challengerL2ArbDb, NewFetcherFromConfig(conf), chainConfig, l1Backend, &challengerRollupAddresses, nil, nil, nil, fatalErrChan, parentChainID) Require(t, err) err = challengerL2.Start(ctx) Require(t, err) diff --git a/system_tests/recreatestate_rpc_test.go b/system_tests/recreatestate_rpc_test.go index 9429155d7c..f5bdca0970 100644 --- a/system_tests/recreatestate_rpc_test.go +++ b/system_tests/recreatestate_rpc_test.go @@ -334,7 +334,8 @@ func testSkippingSavingStateAndRecreatingAfterRestart(t *testing.T, cacheConfig execNode, err := gethexec.CreateExecutionNode(ctx1, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - node, err := arbnode.CreateNode(ctx1, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan) + parentChainID := big.NewInt(1337) + node, err := arbnode.CreateNode(ctx1, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, nil, nil, nil, nil, feedErrChan, parentChainID) Require(t, err) err = node.TxStreamer.AddFakeInitMessage() Require(t, err) @@ -375,7 +376,7 @@ func testSkippingSavingStateAndRecreatingAfterRestart(t *testing.T, cacheConfig execNode, err = gethexec.CreateExecutionNode(ctx1, stack, chainDb, blockchain, nil, execConfigFetcher) Require(t, err) - node, err = arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, node.DeployInfo, nil, nil, nil, feedErrChan) + node, err = arbnode.CreateNode(ctx, stack, execNode, arbDb, NewFetcherFromConfig(arbnode.ConfigDefaultL2Test()), blockchain.Config(), nil, node.DeployInfo, nil, nil, nil, feedErrChan, parentChainID) Require(t, err) Require(t, node.Start(ctx)) client = ClientForStack(t, stack) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index ce726dcf83..6e3ffd6125 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/arbnode/dataposter/externalsignertest" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/solgen/go/mocksgen" @@ -60,7 +61,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) t.Parallel() ctx, cancelCtx := context.WithCancel(context.Background()) defer cancelCtx() - httpSrv, srv := newServer(ctx, t) + httpSrv, srv := externalsignertest.NewServer(ctx, t) + cp, err := externalsignertest.CertPaths() + if err != nil { + t.Fatalf("Error getting cert paths: %v", err) + } t.Cleanup(func() { if err := httpSrv.Shutdown(ctx); err != nil { t.Fatalf("Error shutting down http server: %v", err) @@ -68,7 +73,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) }) go func() { log.Debug("Server is listening on port 1234...") - if err := httpSrv.ListenAndServeTLS(signerServerCert, signerServerKey); err != nil && err != http.ErrServerClosed { + if err := httpSrv.ListenAndServeTLS(cp.ServerCert, cp.ServerKey); err != nil && err != http.ErrServerClosed { log.Debug("ListenAndServeTLS() failed", "error", err) return } @@ -86,10 +91,10 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) cleanupA := builder.Build(t) defer cleanupA() - addNewBatchPoster(ctx, t, builder, srv.address) + addNewBatchPoster(ctx, t, builder, srv.Address) builder.L1.SendWaitTestTransactions(t, []*types.Transaction{ - builder.L1Info.PrepareTxTo("Faucet", &srv.address, 30000, big.NewInt(1).Mul(big.NewInt(1e18), big.NewInt(1e18)), nil)}) + builder.L1Info.PrepareTxTo("Faucet", &srv.Address, 30000, big.NewInt(1).Mul(big.NewInt(1e18), big.NewInt(1e18)), nil)}) l2nodeA := builder.L2.ConsensusNode execNodeA := builder.L2.ExecNode @@ -152,7 +157,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) rollupABI, err := abi.JSON(strings.NewReader(rollupgen.RollupAdminLogicABI)) Require(t, err, "unable to parse rollup ABI") - setValidatorCalldata, err := rollupABI.Pack("setValidator", []common.Address{valWalletAddrA, l1authB.From, srv.address}, []bool{true, true, true}) + setValidatorCalldata, err := rollupABI.Pack("setValidator", []common.Address{valWalletAddrA, l1authB.From, srv.Address}, []bool{true, true, true}) Require(t, err, "unable to generate setValidator calldata") tx, err := upgradeExecutor.ExecuteCall(&deployAuth, l2nodeA.DeployInfo.Rollup, setValidatorCalldata) Require(t, err, "unable to set validators") @@ -170,8 +175,18 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err) valConfig := staker.TestL1ValidatorConfig - - dpA, err := arbnode.StakerDataposter(ctx, rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), l2nodeA.L1Reader, &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil) + parentChainID, err := builder.L1.Client.ChainID(ctx) + if err != nil { + t.Fatalf("Failed to get parent chain id: %v", err) + } + dpA, err := arbnode.StakerDataposter( + ctx, + rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), + l2nodeA.L1Reader, + &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), + nil, + parentChainID, + ) if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } @@ -219,8 +234,19 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) } Require(t, err) cfg := arbnode.ConfigDefaultL1NonSequencerTest() - cfg.Staker.DataPoster.ExternalSigner = *externalSignerTestCfg(srv.address) - dpB, err := arbnode.StakerDataposter(ctx, rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), l2nodeB.L1Reader, &l1authB, NewFetcherFromConfig(cfg), nil) + signerCfg, err := externalSignerTestCfg(srv.Address) + if err != nil { + t.Fatalf("Error getting external signer config: %v", err) + } + cfg.Staker.DataPoster.ExternalSigner = *signerCfg + dpB, err := arbnode.StakerDataposter( + ctx, + rawdb.NewTable(l2nodeB.ArbDB, storage.StakerPrefix), + l2nodeB.L1Reader, + &l1authB, NewFetcherFromConfig(cfg), + nil, + parentChainID, + ) if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } @@ -385,14 +411,14 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") } if faultyStaker { - conflictInfo, err := validatorUtils.FindStakerConflict(&bind.CallOpts{}, l2nodeA.DeployInfo.Rollup, l1authA.From, srv.address, big.NewInt(1024)) + conflictInfo, err := validatorUtils.FindStakerConflict(&bind.CallOpts{}, l2nodeA.DeployInfo.Rollup, l1authA.From, srv.Address, big.NewInt(1024)) Require(t, err) if staker.ConflictType(conflictInfo.Ty) == staker.CONFLICT_TYPE_FOUND { cancelBackgroundTxs() } } if faultyStaker && !sawStakerZombie { - sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, srv.address) + sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, srv.Address) Require(t, err) } isHonestZombie, err := rollup.IsZombie(&bind.CallOpts{}, valWalletAddrA) @@ -413,7 +439,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err) } if !stakerBWasStaked { - stakerBWasStaked, err = rollup.IsStaked(&bind.CallOpts{}, srv.address) + stakerBWasStaked, err = rollup.IsStaked(&bind.CallOpts{}, srv.Address) Require(t, err) } for j := 0; j < 5; j++ {