diff --git a/config/config.go b/config/config.go index 2df62a49..b6b8036f 100644 --- a/config/config.go +++ b/config/config.go @@ -25,10 +25,14 @@ const ( type FullNodeRPCs map[uint32]string +// ProofSigners holds the address for authorized signers of proofs for a given rollup ip +type ProofSigners map[uint32]common.Address + // Config represents the full configuration of the data node type Config struct { FullNodeRPCs FullNodeRPCs `mapstructure:"FullNodeRPCs"` RPC jRPC.Config `mapstructure:"RPC"` + ProofSigners ProofSigners `mapstructure:"ProofSigners"` Log log.Config `mapstructure:"Log"` DB db.Config `mapstructure:"DB"` EthTxManager EthTxManagerConfig `mapstructure:"EthTxManager"` diff --git a/config/default.go b/config/default.go index d2266f65..c44ed988 100644 --- a/config/default.go +++ b/config/default.go @@ -19,6 +19,10 @@ const DefaultValues = ` WriteTimeout = "60s" MaxRequestsPerIPAndSecond = 5000 +# Address should be adjusted +[ProofSigners] +# 1 = "0x0000000000000000000000000000000000000000" + [Log] Environment = "development" # "production" or "development" Level = "debug" diff --git a/docker/data/agglayer/agglayer.toml b/docker/data/agglayer/agglayer.toml index 60783823..7a7aeeaf 100644 --- a/docker/data/agglayer/agglayer.toml +++ b/docker/data/agglayer/agglayer.toml @@ -8,6 +8,10 @@ WriteTimeout = "60s" MaxRequestsPerIPAndSecond = 5000 +# Address should be adjusted +[ProofSigners] +# 1 = "0x0000000000000000000000000000000000000000" + [Log] Environment = "development" # "production" or "development" Level = "debug" diff --git a/interop/executor.go b/interop/executor.go index 9382049f..241067e2 100644 --- a/interop/executor.go +++ b/interop/executor.go @@ -131,12 +131,25 @@ func (e *Executor) verifySignature(stx tx.SignedTx) error { return errors.New("failed to get signer") } - sequencer, err := e.etherman.GetSequencerAddr(stx.Tx.RollupID) - if err != nil { - return errors.New("failed to get admin from L1") - } - if sequencer != signer { - return errors.New("unexpected signer") + // Attempt to retrieve the authorized proof signer for the given rollup, if one exists + authorizedProofSigner, hasKey := e.config.ProofSigners[stx.Tx.RollupID] + + // If an authorized proof signer is defined and matches the signer, no further checks are needed + if hasKey { + // If an authorized proof signer exists but does not match the signer, return an error. + if authorizedProofSigner != signer { + return fmt.Errorf("unexpected signer: expected authorized signer %s, but got %s", authorizedProofSigner, signer) + } + } else { + sequencer, err := e.etherman.GetSequencerAddr(stx.Tx.RollupID) + if err != nil { + return errors.New("failed to get admin from L1") + } + + // If no specific authorized proof signer is defined, fall back to comparing with the sequencer + if sequencer != signer { + return fmt.Errorf("unexpected signer: expected sequencer %s but got %s", sequencer, signer) + } } opts := metric.WithAttributes(attribute.Key("rollup_id").Int(int(stx.Tx.RollupID))) diff --git a/interop/executor_test.go b/interop/executor_test.go index 528b0100..1d27d341 100644 --- a/interop/executor_test.go +++ b/interop/executor_test.go @@ -144,23 +144,72 @@ func TestExecutor_VerifySignature(t *testing.T) { RollupID: 1, } - pk, err := crypto.GenerateKey() + sequencerKey, err := crypto.GenerateKey() require.NoError(t, err) - signedTx, err := txn.Sign(pk) - require.NoError(t, err) + t.Run("use sequencer key, correct signature", func(t *testing.T) { + etherman.On( + "GetSequencerAddr", + uint32(1), + ).Return( + crypto.PubkeyToAddress(sequencerKey.PublicKey), + nil, + ).Once() - etherman.On( - "GetSequencerAddr", - uint32(1), - ).Return( - crypto.PubkeyToAddress(pk.PublicKey), - nil, - ).Once() + signedTx, err := txn.Sign(sequencerKey) + require.NoError(t, err) - err = executor.verifySignature(*signedTx) - require.NoError(t, err) - etherman.AssertExpectations(t) + err = executor.verifySignature(*signedTx) + require.NoError(t, err) + etherman.AssertExpectations(t) + }) + + t.Run("use sequencer key, wrong signature", func(t *testing.T) { + etherman.On( + "GetSequencerAddr", + uint32(1), + ).Return( + common.Address{0x1}, + nil, + ).Once() + + signedTx, err := txn.Sign(sequencerKey) + require.NoError(t, err) + + err = executor.verifySignature(*signedTx) + require.Error(t, err) + etherman.AssertExpectations(t) + }) + + t.Run("configured proof signers, correct signature", func(t *testing.T) { + anotherKey, err := crypto.GenerateKey() + require.NoError(t, err) + + cfg.ProofSigners = config.ProofSigners{1: crypto.PubkeyToAddress(anotherKey.PublicKey)} + + signedTx, err := txn.Sign(anotherKey) + require.NoError(t, err) + + executor = New(nil, cfg, interopAdminAddr, etherman, ethTxManager) + + err = executor.verifySignature(*signedTx) + require.NoError(t, err) + }) + + t.Run("configured proof signers, wrong signature", func(t *testing.T) { + anotherKey, err := crypto.GenerateKey() + require.NoError(t, err) + + cfg.ProofSigners = config.ProofSigners{1: common.Address{0x1}} + + signedTx, err := txn.Sign(anotherKey) + require.NoError(t, err) + + executor = New(nil, cfg, interopAdminAddr, etherman, ethTxManager) + + err = executor.verifySignature(*signedTx) + require.Error(t, err) + }) } func TestExecutor_Execute(t *testing.T) {