diff --git a/cmd/vega/commands/bridge/erc20.go b/cmd/vega/commands/bridge/erc20.go index 7d59f8ef25a..c13a3699bc0 100644 --- a/cmd/vega/commands/bridge/erc20.go +++ b/cmd/vega/commands/bridge/erc20.go @@ -34,7 +34,8 @@ type ERC20Cmd struct { config.VegaHomeFlag config.PassphraseFlag Config nodewallets.Config - PrivateKey string `description:"A ethereum private key to be use to sign the messages" long:"private-key" required:"false"` + PrivateKey string `description:"A ethereum private key to be use to sign the messages" long:"private-key" required:"false"` + ChainID string `description:"The chain-id of the EVM bridge. Not required if generating signatures for the Ethereum bridge" long:"chain-id" required:"false"` AddSigner ERC20AddSignerCmd `command:"add_signer" description:"Create signature to add a new signer to the erc20 bridge"` RemoveSigner ERC20RemoveSignerCmd `command:"remove_signer" description:"Create signature to remove a signer from the erc20 bridge"` @@ -144,7 +145,7 @@ func (opts *ERC20WithdrawAssetCmd) Execute(_ []string) error { creation := time.Unix(opts.Creation, 0) - erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20Logic.WithdrawAsset( opts.TokenAddress, amount, opts.ReceiverAddress, creation, nonce, ) @@ -191,7 +192,7 @@ func (opts *ERC20VerifyWithdrawAssetCmd) Execute(_ []string) error { return errors.New("invalid signatures format") } - erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") addresses, err := erc20Logic.VerifyWithdrawAsset( opts.TokenAddress, amount, opts.ReceiverAddress, creation, nonce, opts.Signatures, ) @@ -238,7 +239,7 @@ func (opts *ERC20ListAssetCmd) Execute(_ []string) error { return errors.New("invalid withdraw-threshold, needs to be base 10") } - erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20Logic.ListAsset( opts.TokenAddress, opts.VegaAssetID, lifetimeLimit, withdrawThreshod, nonce, ) @@ -286,7 +287,7 @@ func (opts *ERC20VerifyListAssetCmd) Execute(_ []string) error { return errors.New("invalid signatures format") } - erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") addresses, err := erc20Logic.VerifyListAsset( opts.TokenAddress, opts.VegaAssetID, lifetimeLimit, withdrawThreshod, nonce, opts.Signatures, ) @@ -322,7 +323,7 @@ func (opts *ERC20RemoveAssetCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10") } - erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20Logic.RemoveAsset( opts.TokenAddress, nonce, ) @@ -355,7 +356,7 @@ func (opts *ERC20AddSignerCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10") } - multiSigControl := bridges.NewERC20MultiSigControl(w) + multiSigControl := bridges.NewERC20MultiSigControl(w, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := multiSigControl.AddSigner( opts.NewSigner, opts.Submitter, nonce, ) @@ -388,7 +389,7 @@ func (opts *ERC20RemoveSignerCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10") } - multiSigControl := bridges.NewERC20MultiSigControl(w) + multiSigControl := bridges.NewERC20MultiSigControl(w, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := multiSigControl.RemoveSigner( opts.OldSigner, opts.Submitter, nonce, ) @@ -425,7 +426,7 @@ func (opts *ERC20SetThresholdCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10 and not overflow") } - multiSigControl := bridges.NewERC20MultiSigControl(w) + multiSigControl := bridges.NewERC20MultiSigControl(w, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := multiSigControl.SetThreshold( opts.NewThreshold, opts.Submitter, nonce, ) @@ -457,7 +458,7 @@ func (opts *ERC20BurnNonceCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10 and not overflow") } - multiSigControl := bridges.NewERC20MultiSigControl(w) + multiSigControl := bridges.NewERC20MultiSigControl(w, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := multiSigControl.BurnNonce(opts.Submitter, nonce) if err != nil { return fmt.Errorf("unable to generate signature: %w", err) @@ -488,7 +489,7 @@ func (opts *ERC20SetBridgeAddressCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10") } - erc20Logic := bridges.NewERC20AssetPool(w, opts.AssetPoolAddress) + erc20Logic := bridges.NewERC20AssetPool(w, opts.AssetPoolAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20Logic.SetBridgeAddress( opts.NewAddress, nonce, ) @@ -521,7 +522,7 @@ func (opts *ERC20SetMultisigControlCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10") } - erc20Logic := bridges.NewERC20AssetPool(w, opts.AssetPoolAddress) + erc20Logic := bridges.NewERC20AssetPool(w, opts.AssetPoolAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20Logic.SetMultiSigControl( opts.NewAddress, nonce, ) @@ -553,7 +554,7 @@ func (opts *ERC20GlobalStopCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10 and not overflow") } - erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20.GlobalStop( nonce, ) @@ -585,7 +586,7 @@ func (opts *ERC20GlobalResumeCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10 and not overflow") } - erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20.GlobalResume( nonce, ) @@ -621,7 +622,7 @@ func (opts *ERC20VerifyGlobalResumeCmd) Execute(_ []string) error { return errors.New("invalid signatures format") } - erc20 := bridges.NewERC20Logic(nil, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(nil, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") signers, err := erc20.VerifyGlobalResume( nonce, opts.Signatures, ) @@ -671,7 +672,7 @@ func (opts *ERC20SetAssetLimitsCmd) Execute(_ []string) error { return errors.New("invalid deposit-lifetime-maximum needs to be base 10 and not overflow") } - erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20.SetAssetLimits( opts.TokenAddress, depositLifetime, threshold, nonce, ) @@ -720,7 +721,7 @@ func (opts *ERC20VerifySetAssetLimitsCmd) Execute(_ []string) error { return errors.New("invalid signatures format") } - erc20 := bridges.NewERC20Logic(nil, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(nil, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") signers, err := erc20.VerifySetAssetLimits( opts.TokenAddress, depositLifetime, threshold, nonce, opts.Signatures, ) @@ -756,7 +757,7 @@ func (opts *ERC20SetWithdrawDelayCmd) Execute(_ []string) error { return errors.New("invalid nonce, needs to be base 10 and not overflow") } - erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress) + erc20 := bridges.NewERC20Logic(w, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") bundle, err := erc20.SetWithdrawDelay( opts.Delay, nonce, ) @@ -793,7 +794,7 @@ func (opts *ERC20VerifySetWithdrawDelayCmd) Execute(_ []string) error { return errors.New("invalid signatures format") } - erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress) + erc20Logic := bridges.NewERC20Logic(nil, opts.BridgeAddress, erc20Cmd.ChainID, erc20Cmd.ChainID == "") addresses, err := erc20Logic.VerifyWithdrawDelay( opts.Delay, nonce, opts.Signatures, ) diff --git a/core/assets/assets_test.go b/core/assets/assets_test.go index 64bf02cdd3d..057ac7da834 100644 --- a/core/assets/assets_test.go +++ b/core/assets/assets_test.go @@ -141,8 +141,10 @@ func getTestService(t *testing.T) *testService { ctrl := gomock.NewController(t) primaryEthClient := erc20mocks.NewMockETHClient(ctrl) primaryEthClient.EXPECT().ChainID(gomock.Any()).AnyTimes().Return(big.NewInt(1), nil) + primaryEthClient.EXPECT().IsEthereum().AnyTimes().Return(true) secondaryEthClient := erc20mocks.NewMockETHClient(ctrl) secondaryEthClient.EXPECT().ChainID(gomock.Any()).AnyTimes().Return(big.NewInt(2), nil) + secondaryEthClient.EXPECT().IsEthereum().AnyTimes().Return(false) broker := bmocks.NewMockInterface(ctrl) primaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) secondaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) diff --git a/core/assets/erc20/erc20.go b/core/assets/erc20/erc20.go index 4c086d9abc9..7377f164189 100644 --- a/core/assets/erc20/erc20.go +++ b/core/assets/erc20/erc20.go @@ -45,6 +45,7 @@ type ETHClient interface { CurrentHeight(context.Context) (uint64, error) ConfirmationsRequired() uint64 ChainID(ctx context.Context) (*big.Int, error) + IsEthereum() bool } type ERC20 struct { @@ -144,7 +145,7 @@ func (e *ERC20) SignListAsset() (msg []byte, sig []byte, err error) { } source := e.asset.Details.GetERC20() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). ListAsset(e.address, e.asset.ID, source.LifetimeLimit, source.WithdrawThreshold, nonce) if err != nil { return nil, nil, err @@ -155,7 +156,7 @@ func (e *ERC20) SignListAsset() (msg []byte, sig []byte, err error) { func (e *ERC20) SignSetAssetLimits(nonce *num.Uint, lifetimeLimit *num.Uint, withdrawThreshold *num.Uint) (msg []byte, sig []byte, err error) { bridgeAddress := e.ethClient.CollateralBridgeAddress().Hex() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). SetAssetLimits(e.address, lifetimeLimit, withdrawThreshold, nonce) if err != nil { return nil, nil, err @@ -172,7 +173,7 @@ func (e *ERC20) SignWithdrawal( ) (msg []byte, sig []byte, err error) { nonce, _ := num.UintFromBig(withdrawRef) bridgeAddress := e.ethClient.CollateralBridgeAddress().Hex() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). WithdrawAsset(e.address, amount, ethPartyAddress, now, nonce) if err != nil { return nil, nil, err diff --git a/core/assets/erc20/erc20_test.go b/core/assets/erc20/erc20_test.go index 29ceb748277..252dd637af6 100644 --- a/core/assets/erc20/erc20_test.go +++ b/core/assets/erc20/erc20_test.go @@ -131,6 +131,10 @@ func (testEthClient) ChainID(context.Context) (*big.Int, error) { return big.NewInt(1), nil } +func (testEthClient) IsEthereum() bool { + return true +} + func (testEthClient) CollateralBridgeAddress() ethcommon.Address { return ethcommon.HexToAddress(bridgeAddress) } diff --git a/core/assets/erc20/mocks/eth_client_mock.go b/core/assets/erc20/mocks/eth_client_mock.go index 5a40a3567ca..1e886d67230 100644 --- a/core/assets/erc20/mocks/eth_client_mock.go +++ b/core/assets/erc20/mocks/eth_client_mock.go @@ -171,6 +171,20 @@ func (mr *MockETHClientMockRecorder) HeaderByNumber(arg0, arg1 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderByNumber", reflect.TypeOf((*MockETHClient)(nil).HeaderByNumber), arg0, arg1) } +// IsEthereum mocks base method. +func (m *MockETHClient) IsEthereum() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsEthereum") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsEthereum indicates an expected call of IsEthereum. +func (mr *MockETHClientMockRecorder) IsEthereum() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEthereum", reflect.TypeOf((*MockETHClient)(nil).IsEthereum)) +} + // PendingCodeAt mocks base method. func (m *MockETHClient) PendingCodeAt(arg0 context.Context, arg1 common.Address) ([]byte, error) { m.ctrl.T.Helper() diff --git a/core/bridges/erc20_asset_pool.go b/core/bridges/erc20_asset_pool.go index 33ea07f7d9b..90e52207f14 100644 --- a/core/bridges/erc20_asset_pool.go +++ b/core/bridges/erc20_asset_pool.go @@ -27,12 +27,16 @@ import ( type ERC20AssetPool struct { signer Signer poolAddr string + chainID string + v1 bool } -func NewERC20AssetPool(signer Signer, poolAddr string) *ERC20AssetPool { +func NewERC20AssetPool(signer Signer, poolAddr, chainID string, v1 bool) *ERC20AssetPool { return &ERC20AssetPool{ signer: signer, poolAddr: poolAddr, + chainID: chainID, + v1: v1, } } @@ -76,7 +80,7 @@ func (e ERC20AssetPool) SetBridgeAddress( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.poolAddr) + msg, err := packScheme(buf, e.poolAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -124,7 +128,7 @@ func (e ERC20AssetPool) SetMultiSigControl( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.poolAddr) + msg, err := packScheme(buf, e.poolAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } diff --git a/core/bridges/erc20_asset_pool_test.go b/core/bridges/erc20_asset_pool_test.go index 9885c036daf..d93162c5b73 100644 --- a/core/bridges/erc20_asset_pool_test.go +++ b/core/bridges/erc20_asset_pool_test.go @@ -29,19 +29,37 @@ const ( ) func TestAssetPoolSetBridgeAddress(t *testing.T) { - signer := testSigner{} - pool := bridges.NewERC20AssetPool(signer, erc20AssetPool) - sig, err := pool.SetBridgeAddress( - erc20AssetAddr, - num.NewUint(42), - ) - - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig.Signature) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "2488c05dd36a754db037f22a1d649109573e299a3c135efdb81c6f64632b26101c0b4ce19c896d370abae8d457682b21a4a3322f48380f29932b311b6ab47707", - sig.Signature.Hex(), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "2488c05dd36a754db037f22a1d649109573e299a3c135efdb81c6f64632b26101c0b4ce19c896d370abae8d457682b21a4a3322f48380f29932b311b6ab47707", + }, + { + name: "v2 scheme", + v1: false, + expected: "4b01dfa1a3b77ecc624f678805a74418862cbcb1e32b929e7dce7fbbfa73806ec1f5db1d40d28f4ebcb09d83f59815f04438142612ebc1683158a23c9fbf3a0c", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(tt *testing.T) { + signer := testSigner{} + pool := bridges.NewERC20AssetPool(signer, erc20AssetPool, chainID, tc.v1) + sig, err := pool.SetBridgeAddress( + erc20AssetAddr, + num.NewUint(42), + ) + + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig.Signature) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + }) + } } diff --git a/core/bridges/erc20_common.go b/core/bridges/erc20_common.go index a450fc684b6..22417e1c781 100644 --- a/core/bridges/erc20_common.go +++ b/core/bridges/erc20_common.go @@ -16,16 +16,20 @@ package bridges import ( + "bytes" "encoding/hex" "fmt" "code.vegaprotocol.io/vega/core/nodewallets/eth/clef" + "code.vegaprotocol.io/vega/libs/num" "github.com/ethereum/go-ethereum/accounts/abi" ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) +var prefix = []byte{0x19} + type Signer interface { Sign([]byte) ([]byte, error) Algo() string @@ -46,7 +50,18 @@ func (b Bytes) Hex() string { return hex.EncodeToString(b) } -func packBufAndSubmitter( +func packScheme( + buf []byte, submitter, chainID string, v1 bool, +) ([]byte, error) { + if v1 { + return packSchemeV1(buf, submitter) + } + return packSchemeV2(buf, submitter, chainID) +} + +// packSchemeV1 returns the payload to be hashed and signed where +// payload = abi.encode(message, msg.sender). +func packSchemeV1( buf []byte, submitter string, ) ([]byte, error) { typBytes, err := abi.NewType("bytes", "", nil) @@ -73,6 +88,44 @@ func packBufAndSubmitter( return args2.Pack(buf, submitterAddr) } +// packSchemeV2 returns the payload to be hashed and signed where +// payload = abi.encodePacked(bytes1(0x19), block.chainid, abi.encode(message, msg.sender)) +// where abi.encodePacked is the concatenation of the individual byte slices. +func packSchemeV2( + buf []byte, submitter, chainID string, +) ([]byte, error) { + typBytes, err := abi.NewType("bytes", "", nil) + if err != nil { + return nil, err + } + typAddr, err := abi.NewType("address", "", nil) + if err != nil { + return nil, err + } + + submitterAddr := ethcmn.HexToAddress(submitter) + args := abi.Arguments([]abi.Argument{ + { + Name: "bytes", + Type: typBytes, + }, + { + Name: "address", + Type: typAddr, + }, + }) + + // abi.encode(message, msg.sender) + buf, err = args.Pack(buf, submitterAddr) + if err != nil { + return nil, err + } + + // concat(prefix, chain-id, abi.encode(message, msg.sender)) + cid := num.MustUintFromString(chainID, 10).Bytes() + return bytes.Join([][]byte{prefix, cid[:], buf}, nil), nil +} + func sign(signer Signer, msg []byte) (*SignaturePayload, error) { var sig []byte var err error diff --git a/core/bridges/erc20_logic.go b/core/bridges/erc20_logic.go index abee5961894..434aee3e0b6 100644 --- a/core/bridges/erc20_logic.go +++ b/core/bridges/erc20_logic.go @@ -33,12 +33,16 @@ import ( type ERC20Logic struct { signer Signer bridgeAddr string + chainID string + v1 bool } -func NewERC20Logic(signer Signer, bridgeAddr string) *ERC20Logic { +func NewERC20Logic(signer Signer, bridgeAddr string, chainID string, v1 bool) *ERC20Logic { return &ERC20Logic{ signer: signer, bridgeAddr: bridgeAddr, + chainID: chainID, + v1: v1, } } @@ -109,7 +113,7 @@ func (e ERC20Logic) ListAsset( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -184,7 +188,7 @@ func (e ERC20Logic) buildListAssetMessage( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -268,7 +272,7 @@ func (e ERC20Logic) RemoveAsset( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -356,7 +360,7 @@ func (e ERC20Logic) buildWithdrawAssetMessage( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - return packBufAndSubmitter(buf, e.bridgeAddr) + return packScheme(buf, e.bridgeAddr, e.chainID, e.v1) } func (e ERC20Logic) VerifyWithdrawAsset( @@ -449,7 +453,7 @@ func (e ERC20Logic) SetAssetLimits( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -511,7 +515,7 @@ func (e ERC20Logic) buildSetAssetLimitsMessage( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -606,7 +610,7 @@ func (e ERC20Logic) buildWithdrawDelayMessage( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - return packBufAndSubmitter(buf, e.bridgeAddr) + return packScheme(buf, e.bridgeAddr, e.chainID, e.v1) } func (e ERC20Logic) VerifyWithdrawDelay( @@ -673,7 +677,7 @@ func (e ERC20Logic) GlobalStop( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -712,7 +716,7 @@ func (e ERC20Logic) GlobalResume( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } @@ -752,7 +756,7 @@ func (e ERC20Logic) VerifyGlobalResume( return nil, fmt.Errorf("couldn't pack abi message: %w", err) } - msg, err := packBufAndSubmitter(buf, e.bridgeAddr) + msg, err := packScheme(buf, e.bridgeAddr, e.chainID, e.v1) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) } diff --git a/core/bridges/erc20_logic_test.go b/core/bridges/erc20_logic_test.go index f147ddcc449..e1d97c24674 100644 --- a/core/bridges/erc20_logic_test.go +++ b/core/bridges/erc20_logic_test.go @@ -39,61 +39,115 @@ func TestERC20Logic(t *testing.T) { } func testListAsset(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr) - sig, err := bridge.ListAsset( - erc20AssetAddr, - erc20AssetVegaID, - num.NewUint(10), - num.NewUint(42), - num.NewUint(42), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "7df8b88552c2f981e64b13f1ce3ee5dcb71e8f59ec057010b7b469120afff7d479f234714785cfc605230dfb2d17f9cc7858143196a13f357ce008e3f3f78a00", + }, + { + name: "v2 scheme", + v1: false, + expected: "03d8d648da4402bebd096f067cebf3e3b70f2c4e1cad6ca9eb757f554b6ca9efb84010887aeef543cf72cb5d78a741d0683befc6f5e0ca2d0347832232af610c", + }, + } - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig.Signature) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "7df8b88552c2f981e64b13f1ce3ee5dcb71e8f59ec057010b7b469120afff7d479f234714785cfc605230dfb2d17f9cc7858143196a13f357ce008e3f3f78a00", - sig.Signature.Hex(), - ) + for _, tc := range tcs { + t.Run(tc.name, func(tt *testing.T) { + signer := testSigner{} + bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr, chainID, tc.v1) + sig, err := bridge.ListAsset( + erc20AssetAddr, + erc20AssetVegaID, + num.NewUint(10), + num.NewUint(42), + num.NewUint(42), + ) + + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig.Signature) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + }) + } } func testRemoveAsset(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr) - sig, err := bridge.RemoveAsset( - erc20AssetAddr, - num.NewUint(42), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "9012eb20763500caf1a4d7640470449c7220872d7136e17c70231c269051cf80e08760d60850578ebf494e24610a54225c7d994f15f57d9f451e8f717eb3f904", + }, + { + name: "v2 scheme", + v1: false, + expected: "aa07e175a9a4c3dcb0f5dcbd24cc6636e699ee6a1daa9a80267cec8f0be130b86465fa56296743879f56d94d6be64a0b10b76bcee40d0d09ec078b2814b89500", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(tt *testing.T) { + signer := testSigner{} + bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr, chainID, tc.v1) + sig, err := bridge.RemoveAsset( + erc20AssetAddr, + num.NewUint(42), + ) - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig.Signature) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "9012eb20763500caf1a4d7640470449c7220872d7136e17c70231c269051cf80e08760d60850578ebf494e24610a54225c7d994f15f57d9f451e8f717eb3f904", - sig.Signature.Hex(), - ) + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig.Signature) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + }) + } } func testWithdrawAsset(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr) - sig, err := bridge.WithdrawAsset( - erc20AssetAddr, - num.NewUint(42), // amount - ethPartyAddr, - time.Unix(1000, 0), - num.NewUint(1000), // nonce - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "0ff08571ab504acdce063a5a5a00dd8878d64ccb09ea6887aacd1fd41b517cd13f4e12edfaa4d06fef5d24087ba9e7c980532daa0a6f1fa329b8d75961f4ab03", + }, + { + name: "v2 scheme", + v1: false, + expected: "9f2d7ec17059fd5d4697337a46899f73681dece748ea1342b3be24b5f34f0b934ad448f7e9bd3a113102d46d8433dd26458cf06c3fd7a1622d086faab1a77b08", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(tt *testing.T) { + signer := testSigner{} + bridge := bridges.NewERC20Logic(signer, erc20BridgeAddr, chainID, tc.v1) + sig, err := bridge.WithdrawAsset( + erc20AssetAddr, + num.NewUint(42), // amount + ethPartyAddr, + time.Unix(1000, 0), + num.NewUint(1000), // nonce + ) - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig.Signature) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "0ff08571ab504acdce063a5a5a00dd8878d64ccb09ea6887aacd1fd41b517cd13f4e12edfaa4d06fef5d24087ba9e7c980532daa0a6f1fa329b8d75961f4ab03", - sig.Signature.Hex(), - ) + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig.Signature) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + }) + } } diff --git a/core/bridges/erc20_multisigcontrol.go b/core/bridges/erc20_multisigcontrol.go index 4b8f018ecaa..177c30a14aa 100644 --- a/core/bridges/erc20_multisigcontrol.go +++ b/core/bridges/erc20_multisigcontrol.go @@ -23,12 +23,16 @@ import ( ) type ERC20MultiSigControl struct { - signer Signer + signer Signer + chainID string + v1 bool } -func NewERC20MultiSigControl(signer Signer) *ERC20MultiSigControl { +func NewERC20MultiSigControl(signer Signer, chainID string, v1 bool) *ERC20MultiSigControl { return &ERC20MultiSigControl{ - signer: signer, + signer: signer, + chainID: chainID, + v1: v1, } } @@ -61,7 +65,7 @@ func (e *ERC20MultiSigControl) BurnNonce( return nil, err } - msg, err := packBufAndSubmitter(buf, submitter) + msg, err := packScheme(buf, submitter, e.chainID, e.v1) if err != nil { return nil, err } @@ -107,11 +111,10 @@ func (e *ERC20MultiSigControl) SetThreshold( return nil, err } - msg, err := packBufAndSubmitter(buf, submitter) + msg, err := packScheme(buf, submitter, e.chainID, e.v1) if err != nil { return nil, err } - return sign(e.signer, msg) } @@ -153,7 +156,7 @@ func (e *ERC20MultiSigControl) AddSigner( return nil, err } - msg, err := packBufAndSubmitter(buf, submitter) + msg, err := packScheme(buf, submitter, e.chainID, e.v1) if err != nil { return nil, err } @@ -199,7 +202,7 @@ func (e *ERC20MultiSigControl) RemoveSigner( return nil, err } - msg, err := packBufAndSubmitter(buf, submitter) + msg, err := packScheme(buf, submitter, e.chainID, e.v1) if err != nil { return nil, err } diff --git a/core/bridges/erc20_multisigcontrol_test.go b/core/bridges/erc20_multisigcontrol_test.go index 35f0c36b0cf..998b24e760d 100644 --- a/core/bridges/erc20_multisigcontrol_test.go +++ b/core/bridges/erc20_multisigcontrol_test.go @@ -30,6 +30,8 @@ import ( const ( privKey = "9feb9cbee69c1eeb30db084544ff8bf92166bf3fddefa6a021b458b4de04c66758a127387b1dff15b71fd7d0a9fd104ed75da4aac549efd5d149051ea57cefaf" pubKey = "58a127387b1dff15b71fd7d0a9fd104ed75da4aac549efd5d149051ea57cefaf" + + chainID = "31337" ) func TestERC20MultiSigControl(t *testing.T) { @@ -39,61 +41,109 @@ func TestERC20MultiSigControl(t *testing.T) { } func testSetThreshold(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20MultiSigControl(signer) - sig, err := bridge.SetThreshold( - 1000, - "0x1FaA74E181092A97Fecc923015293ce57eE1208A", - num.NewUint(42), - ) - - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig.Signature) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "a2c61b473f15a1729e8593d65748e7a9813102e0d7304598af556525206db599fb79b9750349c6cb564a2f3ecdf233dd19b1598302e0cb91218adff1c609ac09", - sig.Signature.Hex(), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "a2c61b473f15a1729e8593d65748e7a9813102e0d7304598af556525206db599fb79b9750349c6cb564a2f3ecdf233dd19b1598302e0cb91218adff1c609ac09", + }, + { + name: "v2 scheme", + v1: false, + expected: "aa79559d350a9b139d04d7883b7ec26b3948bba503fddcc55f8a868a69ef48dad32ffb4233a041401e482e71232fc339aa6deffda31bcd978596a6a0a6d64b0c", + }, + } + + for _, tc := range tcs { + signer := testSigner{} + bridge := bridges.NewERC20MultiSigControl(signer, chainID, tc.v1) + sig, err := bridge.SetThreshold( + 1000, + "0x1FaA74E181092A97Fecc923015293ce57eE1208A", + num.NewUint(42), + ) + + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig.Signature) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + } } func testAddSigner(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20MultiSigControl(signer) - sig, err := bridge.AddSigner( - "0xE20c747a7389B7De2c595658277132f188A074EE", - "0x1FaA74E181092A97Fecc923015293ce57eE1208A", - num.NewUint(42), - ) - - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - - assert.Equal(t, - "7bdc018935610f23667b31d4eee248160ab39caa1e70ad20da49bf8971d5a16b30f71a09d9aaf5b532defdb7710d85c226e98cb90a49bc4b4401b33f3c5a1601", - sig.Signature.Hex(), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "7bdc018935610f23667b31d4eee248160ab39caa1e70ad20da49bf8971d5a16b30f71a09d9aaf5b532defdb7710d85c226e98cb90a49bc4b4401b33f3c5a1601", + }, + { + name: "v2 scheme", + v1: false, + expected: "f86654970ab8aa7b8f1ac72cd1349cd667acd21b7ff2078653d488f3ab65a446df1b4878692d7f07e2f0111bed069fd7cf5c32f07ae88ed059624480cd0edd07", + }, + } + + for _, tc := range tcs { + signer := testSigner{} + bridge := bridges.NewERC20MultiSigControl(signer, chainID, tc.v1) + sig, err := bridge.AddSigner( + "0xE20c747a7389B7De2c595658277132f188A074EE", + "0x1FaA74E181092A97Fecc923015293ce57eE1208A", + num.NewUint(42), + ) + + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + + assert.Equal(t, tc.expected, sig.Signature.Hex()) + } } func testRemoveSigner(t *testing.T) { - signer := testSigner{} - bridge := bridges.NewERC20MultiSigControl(signer) - sig, err := bridge.RemoveSigner( - "0xE20c747a7389B7De2c595658277132f188A074EE", - "0x1FaA74E181092A97Fecc923015293ce57eE1208A", - num.NewUint(42), - ) - - assert.NoError(t, err) - assert.NotNil(t, sig.Message) - assert.NotNil(t, sig) - assert.True(t, signer.Verify(sig.Message, sig.Signature)) - assert.Equal(t, - "98ea2303c68dbb0a88bdb7dad8c6e2db9698cd992667399a378e682dbdf16e74a9d304a32e36b48de81c0e99449a7a37c1a7ef94af1e85aa88a808f8d7126c0c", - sig.Signature.Hex(), - ) + tcs := []struct { + name string + v1 bool + expected string + }{ + { + name: "v1 scheme", + v1: true, + expected: "98ea2303c68dbb0a88bdb7dad8c6e2db9698cd992667399a378e682dbdf16e74a9d304a32e36b48de81c0e99449a7a37c1a7ef94af1e85aa88a808f8d7126c0c", + }, + { + name: "v2 scheme", + v1: false, + expected: "e17efd360ce488a7299175473f257544391e3823db314e31cc69e6ae2730ead994e89bfab5813ea1379c4b6e499d131308ebe516ba6142f9f77479083685020b", + }, + } + + for _, tc := range tcs { + signer := testSigner{} + bridge := bridges.NewERC20MultiSigControl(signer, chainID, tc.v1) + sig, err := bridge.RemoveSigner( + "0xE20c747a7389B7De2c595658277132f188A074EE", + "0x1FaA74E181092A97Fecc923015293ce57eE1208A", + num.NewUint(42), + ) + + assert.NoError(t, err) + assert.NotNil(t, sig.Message) + assert.NotNil(t, sig) + assert.True(t, signer.Verify(sig.Message, sig.Signature)) + assert.Equal(t, tc.expected, sig.Signature.Hex()) + } } type testSigner struct{} diff --git a/core/client/eth/client.go b/core/client/eth/client.go index 77a2c67e123..faae6b04690 100644 --- a/core/client/eth/client.go +++ b/core/client/eth/client.go @@ -117,6 +117,11 @@ func (c *PrimaryClient) CollateralBridgeAddressHex() string { return c.ethConfig.CollateralBridge().HexAddress() } +// IsEthereum returns whether or not this client is the "primary" one and pointing to Ethereum. +func (c *PrimaryClient) IsEthereum() bool { + return true +} + func (c *PrimaryClient) CurrentHeight(ctx context.Context) (uint64, error) { c.mu.Lock() defer c.mu.Unlock() diff --git a/core/client/eth/secondary_client.go b/core/client/eth/secondary_client.go index f1d6fb24fc7..529d929cbe5 100644 --- a/core/client/eth/secondary_client.go +++ b/core/client/eth/secondary_client.go @@ -101,6 +101,11 @@ func (c *SecondaryClient) CollateralBridgeAddressHex() string { return c.ethConfig.CollateralBridge().HexAddress() } +// IsEthereum returns whether or not this client is the "primary" one and pointing to Ethereum. +func (c *SecondaryClient) IsEthereum() bool { + return false +} + func (c *SecondaryClient) CurrentHeight(ctx context.Context) (uint64, error) { c.mu.Lock() defer c.mu.Unlock() diff --git a/core/validators/signatures.go b/core/validators/signatures.go index 3097e827517..287a4860a3a 100644 --- a/core/validators/signatures.go +++ b/core/validators/signatures.go @@ -77,15 +77,16 @@ type signatureWithSubmitter struct { } type ERC20Signatures struct { - log *logging.Logger - notary Notary - primaryMultisig MultiSigTopology - primaryBridge *bridges.ERC20MultiSigControl + log *logging.Logger + notary Notary + primaryMultisig MultiSigTopology + // primaryBridge *bridges.ERC20MultiSigControl secondaryMultisig MultiSigTopology - secondaryBridge *bridges.ERC20MultiSigControl - lastNonce *num.Uint - broker Broker - isValidatorSetup bool + // secondaryBridge *bridges.ERC20MultiSigControl + lastNonce *num.Uint + broker Broker + isValidatorSetup bool + signer Signer // stored nonce's etc. to be able to generate signatures to remove/add an ethereum address from the multisig bundle pendingSignatures map[string]*signatureData @@ -113,10 +114,7 @@ func NewSignatures( issuedSignatures: map[string]issuedSignature{}, } if isValidatorSetup { - s.primaryBridge = bridges.NewERC20MultiSigControl(nw.GetEthereum()) - // TODO the multisig on bridge 2 will likely mix the chainID in the message to sign - // when we know more about that we will need to pass it in here and use it. - s.secondaryBridge = bridges.NewERC20MultiSigControl(nw.GetEthereum()) + s.signer = nw.GetEthereum() } return s } @@ -133,15 +131,15 @@ type NodeIDAddress struct { SubmitterAddress string } -func (s *ERC20Signatures) getBridge(chainID string) (*bridges.ERC20MultiSigControl, error) { +// isBridge returns whether the given chainID corresponds to one of the bridges, and returns if it is the Ethereum bridge. +func (s *ERC20Signatures) isBridge(chainID string) (isBridge bool, isEthereum bool) { switch chainID { case s.primaryMultisig.ChainID(): - return s.primaryBridge, nil + isBridge, isEthereum = true, true case s.secondaryMultisig.ChainID(): - return s.secondaryBridge, nil - default: - return nil, ErrUnknownChainID + isBridge, isEthereum = true, false } + return } func (s *ERC20Signatures) OfferSignatures() { @@ -181,12 +179,12 @@ func (s *ERC20Signatures) offerValidatorAddedSignatures(resID string) []byte { s.log.Panic("expected added signature but got removed signature instead", logging.String("ethereumAddress", sig.EthAddress)) } - bridge, err := s.getBridge(sig.chainID) - if err != nil { + isBridge, isEthereum := s.isBridge(sig.chainID) + if !isBridge { s.log.Panic("unexpected bridge chainID", logging.String("chain-id", sig.chainID)) } - signature, err := bridge.AddSigner(sig.EthAddress, sig.SubmitterAddress, sig.Nonce.Clone()) + signature, err := bridges.NewERC20MultiSigControl(s.signer, sig.chainID, isEthereum).AddSigner(sig.EthAddress, sig.SubmitterAddress, sig.Nonce.Clone()) if err != nil { s.log.Panic("could not sign remove signer event, wallet not configured properly", logging.Error(err)) @@ -209,12 +207,12 @@ func (s *ERC20Signatures) offerValidatorRemovedSignatures(resID string) []byte { s.log.Panic("expected removed signature but got added signature instead", logging.String("ethereumAddress", sig.EthAddress)) } - bridge, err := s.getBridge(sig.chainID) - if err != nil { + isBridge, isEthereum := s.isBridge(sig.chainID) + if !isBridge { s.log.Panic("unexpected bridge chainID", logging.String("chain-id", sig.chainID)) } - signature, err := bridge.RemoveSigner(sig.EthAddress, sig.SubmitterAddress, sig.Nonce.Clone()) + signature, err := bridges.NewERC20MultiSigControl(s.signer, sig.chainID, isEthereum).RemoveSigner(sig.EthAddress, sig.SubmitterAddress, sig.Nonce.Clone()) if err != nil { s.log.Panic("could not sign remove signer event, wallet not configured properly", logging.Error(err)) @@ -337,9 +335,9 @@ func (s *ERC20Signatures) EmitValidatorAddedSignatures(ctx context.Context, subm return ErrNoPendingSignaturesForNodeID } - bridge, err := s.getBridge(chainID) - if err != nil { - return err + isBridge, isEthereum := s.isBridge(chainID) + if !isBridge { + return ErrUnknownChainID } evts := []events.Event{} @@ -359,7 +357,7 @@ func (s *ERC20Signatures) EmitValidatorAddedSignatures(ctx context.Context, subm } if s.isValidatorSetup { - signature, err := bridge.AddSigner(pending.EthAddress, submitter, nonce) + signature, err := bridges.NewERC20MultiSigControl(s.signer, chainID, isEthereum).AddSigner(pending.EthAddress, submitter, nonce) if err != nil { s.log.Panic("could not sign remove signer event, wallet not configured properly", logging.Error(err)) @@ -400,9 +398,9 @@ func (s *ERC20Signatures) EmitValidatorRemovedSignatures(ctx context.Context, su return ErrNoPendingSignaturesForNodeID } - bridge, err := s.getBridge(chainID) - if err != nil { - return err + isBridge, isEthereum := s.isBridge(chainID) + if !isBridge { + return ErrUnknownChainID } evts := []events.Event{} @@ -421,7 +419,7 @@ func (s *ERC20Signatures) EmitValidatorRemovedSignatures(ctx context.Context, su } if s.isValidatorSetup { - signature, err := bridge.RemoveSigner(pending.EthAddress, submitter, nonce) + signature, err := bridges.NewERC20MultiSigControl(s.signer, chainID, isEthereum).RemoveSigner(pending.EthAddress, submitter, nonce) if err != nil { s.log.Panic("could not sign remove signer event, wallet not configured properly", logging.Error(err))