diff --git a/Dockerfile b/Dockerfile index fcc29486..3d7de192 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,10 +8,8 @@ RUN cd /src && echo $(ls -1 /src) RUN go mod download RUN go build -o /bridge ./e2e/evm-evm/example/. -# # final stage +# final stage FROM debian:stretch-slim -RUN apt-get -y update && apt-get -y upgrade && apt-get install ca-certificates wget -y - COPY --from=builder /bridge ./ RUN chmod +x ./bridge diff --git a/Makefile b/Makefile index 9a397a12..d4ce7b20 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ genmocks: mockgen -destination=./chains/evm/evmgaspricer/mock/gas-pricer.go -source=./chains/evm/evmgaspricer/gas-pricer.go mockgen -destination=./relayer/mock/relayer.go -source=./relayer/relayer.go mockgen -source=chains/evm/calls/utils.go -destination=chains/evm/calls/mock/utils.go + mockgen -destination=chains/evm/voter/mock/voter.go github.com/ChainSafe/chainbridge-core/chains/evm/voter ChainClient,MessageHandler e2e-setup: docker-compose --file=./e2e/evm-evm/docker-compose.e2e.yml up diff --git a/chains/evm/calls/bridge.go b/chains/evm/calls/bridge.go index 74064c60..29f53b59 100644 --- a/chains/evm/calls/bridge.go +++ b/chains/evm/calls/bridge.go @@ -291,32 +291,25 @@ func GetThreshold(evmCaller ContractCallerClient, bridgeAddress *common.Address) func ProposalStatus(evmCaller ContractCallerClient, p *proposal.Proposal) (message.ProposalStatus, error) { a, err := abi.JSON(strings.NewReader(consts.BridgeABI)) if err != nil { - return message.ProposalStatusInactive, err + return message.ProposalStatus{}, err } - input, err := a.Pack("getProposal", p.Source, p.DepositNonce, SliceTo32Bytes(p.Data)) + input, err := a.Pack("getProposal", p.Source, p.DepositNonce, p.GetDataHash()) if err != nil { - return message.ProposalStatusInactive, err + return message.ProposalStatus{}, err } msg := ethereum.CallMsg{From: common.Address{}, To: &p.BridgeAddress, Data: input} out, err := evmCaller.CallContract(context.TODO(), ToCallArg(msg), nil) if err != nil { - return message.ProposalStatusInactive, err - } - - type bridgeProposal struct { - Status uint8 - YesVotes *big.Int - YesVotesTotal uint8 - ProposedBlock *big.Int + return message.ProposalStatus{}, err } res, err := a.Unpack("getProposal", out) if err != nil { - return message.ProposalStatusInactive, err + return message.ProposalStatus{}, err } - out0 := *abi.ConvertType(res[0], new(bridgeProposal)).(*bridgeProposal) - return message.ProposalStatus(out0.Status), nil + ps := *abi.ConvertType(res[0], new(message.ProposalStatus)).(*message.ProposalStatus) + return ps, nil } func idAndNonce(srcId uint8, nonce uint64) *big.Int { diff --git a/chains/evm/calls/bridge_test.go b/chains/evm/calls/bridge_test.go index a0a128c7..d3e7feaa 100644 --- a/chains/evm/calls/bridge_test.go +++ b/chains/evm/calls/bridge_test.go @@ -51,7 +51,7 @@ func (s *ProposalStatusTestSuite) TestProposalStatusFailedContractCall() { status, err := calls.ProposalStatus(s.mockContractCaller, &proposal.Proposal{}) - s.Equal(message.ProposalStatusInactive, status) + s.Equal(message.ProposalStatus{}, status) s.NotNil(err) } @@ -60,18 +60,19 @@ func (s *ProposalStatusTestSuite) TestProposalStatusFailedUnpack() { status, err := calls.ProposalStatus(s.mockContractCaller, &proposal.Proposal{}) - s.Equal(message.ProposalStatusInactive, status) + s.Equal(message.ProposalStatus{}, status) s.NotNil(err) } func (s *ProposalStatusTestSuite) TestProposalStatusSuccessfulCall() { - proposalStatus, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + proposalStatus, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001f") s.mockContractCaller.EXPECT().CallContract(gomock.Any(), gomock.Any(), nil).Return(proposalStatus, nil) status, err := calls.ProposalStatus(s.mockContractCaller, &proposal.Proposal{}) - s.Equal(message.ProposalStatusInactive, status) s.Nil(err) + s.Equal(status.YesVotesTotal, uint8(3)) + s.Equal(status.Status, message.ProposalStatusExecuted) } func TestPrepareWithdrawInput(t *testing.T) { diff --git a/chains/evm/calls/mock/utils.go b/chains/evm/calls/mock/utils.go index 7b5a59a5..a1bd1494 100644 --- a/chains/evm/calls/mock/utils.go +++ b/chains/evm/calls/mock/utils.go @@ -279,6 +279,141 @@ func (mr *MockClientDeployerMockRecorder) WaitAndReturnTxReceipt(h interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndReturnTxReceipt", reflect.TypeOf((*MockClientDeployer)(nil).WaitAndReturnTxReceipt), h) } +// MockContractCallerDispatcherClient is a mock of ContractCallerDispatcherClient interface. +type MockContractCallerDispatcherClient struct { + ctrl *gomock.Controller + recorder *MockContractCallerDispatcherClientMockRecorder +} + +// MockContractCallerDispatcherClientMockRecorder is the mock recorder for MockContractCallerDispatcherClient. +type MockContractCallerDispatcherClientMockRecorder struct { + mock *MockContractCallerDispatcherClient +} + +// NewMockContractCallerDispatcherClient creates a new mock instance. +func NewMockContractCallerDispatcherClient(ctrl *gomock.Controller) *MockContractCallerDispatcherClient { + mock := &MockContractCallerDispatcherClient{ctrl: ctrl} + mock.recorder = &MockContractCallerDispatcherClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockContractCallerDispatcherClient) EXPECT() *MockContractCallerDispatcherClientMockRecorder { + return m.recorder +} + +// CallContract mocks base method. +func (m *MockContractCallerDispatcherClient) CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", ctx, callArgs, blockNumber) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockContractCallerDispatcherClientMockRecorder) CallContract(ctx, callArgs, blockNumber interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).CallContract), ctx, callArgs, blockNumber) +} + +// From mocks base method. +func (m *MockContractCallerDispatcherClient) From() common.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "From") + ret0, _ := ret[0].(common.Address) + return ret0 +} + +// From indicates an expected call of From. +func (mr *MockContractCallerDispatcherClientMockRecorder) From() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "From", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).From)) +} + +// LockNonce mocks base method. +func (m *MockContractCallerDispatcherClient) LockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "LockNonce") +} + +// LockNonce indicates an expected call of LockNonce. +func (mr *MockContractCallerDispatcherClientMockRecorder) LockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockNonce", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).LockNonce)) +} + +// SignAndSendTransaction mocks base method. +func (m *MockContractCallerDispatcherClient) SignAndSendTransaction(ctx context.Context, tx evmclient.CommonTransaction) (common.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignAndSendTransaction", ctx, tx) + ret0, _ := ret[0].(common.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignAndSendTransaction indicates an expected call of SignAndSendTransaction. +func (mr *MockContractCallerDispatcherClientMockRecorder) SignAndSendTransaction(ctx, tx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignAndSendTransaction", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).SignAndSendTransaction), ctx, tx) +} + +// UnlockNonce mocks base method. +func (m *MockContractCallerDispatcherClient) UnlockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnlockNonce") +} + +// UnlockNonce indicates an expected call of UnlockNonce. +func (mr *MockContractCallerDispatcherClientMockRecorder) UnlockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlockNonce", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).UnlockNonce)) +} + +// UnsafeIncreaseNonce mocks base method. +func (m *MockContractCallerDispatcherClient) UnsafeIncreaseNonce() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeIncreaseNonce") + ret0, _ := ret[0].(error) + return ret0 +} + +// UnsafeIncreaseNonce indicates an expected call of UnsafeIncreaseNonce. +func (mr *MockContractCallerDispatcherClientMockRecorder) UnsafeIncreaseNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeIncreaseNonce", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).UnsafeIncreaseNonce)) +} + +// UnsafeNonce mocks base method. +func (m *MockContractCallerDispatcherClient) UnsafeNonce() (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeNonce") + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnsafeNonce indicates an expected call of UnsafeNonce. +func (mr *MockContractCallerDispatcherClientMockRecorder) UnsafeNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeNonce", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).UnsafeNonce)) +} + +// WaitAndReturnTxReceipt mocks base method. +func (m *MockContractCallerDispatcherClient) WaitAndReturnTxReceipt(h common.Hash) (*types.Receipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitAndReturnTxReceipt", h) + ret0, _ := ret[0].(*types.Receipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitAndReturnTxReceipt indicates an expected call of WaitAndReturnTxReceipt. +func (mr *MockContractCallerDispatcherClientMockRecorder) WaitAndReturnTxReceipt(h interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndReturnTxReceipt", reflect.TypeOf((*MockContractCallerDispatcherClient)(nil).WaitAndReturnTxReceipt), h) +} + // MockGasPricer is a mock of GasPricer interface. type MockGasPricer struct { ctrl *gomock.Controller diff --git a/chains/evm/evmclient/evm-client.go b/chains/evm/evmclient/evm-client.go index 30a66c38..4907d801 100644 --- a/chains/evm/evmclient/evm-client.go +++ b/chains/evm/evmclient/evm-client.go @@ -23,16 +23,18 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/rs/zerolog/log" ) type EVMClient struct { *ethclient.Client - rpClient *rpc.Client - config *EVMConfig - nonce *big.Int - nonceLock sync.Mutex + gethClient *gethclient.Client + rpClient *rpc.Client + config *EVMConfig + nonce *big.Int + nonceLock sync.Mutex } // DepositLogs struct holds event data with all necessary parameters and a handler response @@ -74,12 +76,17 @@ func NewEVMClientFromParams(url string, privateKey *ecdsa.PrivateKey) (*EVMClien kp := secp256k1.NewKeypair(*privateKey) c := &EVMClient{} c.Client = ethclient.NewClient(rpcClient) + c.gethClient = gethclient.New(rpcClient) c.rpClient = rpcClient c.config = &EVMConfig{} c.config.kp = kp return c, nil } +func (c *EVMClient) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) { + return c.gethClient.SubscribePendingTransactions(ctx, ch) +} + func (c *EVMClient) Configurate(path string, name string) error { rawCfg, err := GetConfig(path, name) if err != nil { @@ -106,6 +113,7 @@ func (c *EVMClient) Configurate(path string, name string) error { } c.Client = ethclient.NewClient(rpcClient) c.rpClient = rpcClient + c.gethClient = gethclient.New(rpcClient) if generalConfig.LatestBlock { curr, err := c.LatestBlock() @@ -154,7 +162,6 @@ func (c *EVMClient) WaitAndReturnTxReceipt(h common.Hash) (*types.Receipt, error for retry > 0 { receipt, err := c.Client.TransactionReceipt(context.Background(), h) if err != nil { - log.Error().Err(err).Msgf("error getting tx receipt %s", h.String()) retry-- time.Sleep(5 * time.Second) continue diff --git a/chains/evm/voter/mock/voter.go b/chains/evm/voter/mock/voter.go new file mode 100644 index 00000000..29735ce8 --- /dev/null +++ b/chains/evm/voter/mock/voter.go @@ -0,0 +1,237 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/chainbridge-core/chains/evm/voter (interfaces: ChainClient,MessageHandler) + +// Package mock_voter is a generated GoMock package. +package mock_voter + +import ( + context "context" + big "math/big" + reflect "reflect" + + evmclient "github.com/ChainSafe/chainbridge-core/chains/evm/evmclient" + proposal "github.com/ChainSafe/chainbridge-core/chains/evm/voter/proposal" + message "github.com/ChainSafe/chainbridge-core/relayer/message" + common "github.com/ethereum/go-ethereum/common" + types "github.com/ethereum/go-ethereum/core/types" + rpc "github.com/ethereum/go-ethereum/rpc" + gomock "github.com/golang/mock/gomock" +) + +// MockChainClient is a mock of ChainClient interface. +type MockChainClient struct { + ctrl *gomock.Controller + recorder *MockChainClientMockRecorder +} + +// MockChainClientMockRecorder is the mock recorder for MockChainClient. +type MockChainClientMockRecorder struct { + mock *MockChainClient +} + +// NewMockChainClient creates a new mock instance. +func NewMockChainClient(ctrl *gomock.Controller) *MockChainClient { + mock := &MockChainClient{ctrl: ctrl} + mock.recorder = &MockChainClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockChainClient) EXPECT() *MockChainClientMockRecorder { + return m.recorder +} + +// CallContract mocks base method. +func (m *MockChainClient) CallContract(arg0 context.Context, arg1 map[string]interface{}, arg2 *big.Int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CallContract", arg0, arg1, arg2) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CallContract indicates an expected call of CallContract. +func (mr *MockChainClientMockRecorder) CallContract(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallContract", reflect.TypeOf((*MockChainClient)(nil).CallContract), arg0, arg1, arg2) +} + +// From mocks base method. +func (m *MockChainClient) From() common.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "From") + ret0, _ := ret[0].(common.Address) + return ret0 +} + +// From indicates an expected call of From. +func (mr *MockChainClientMockRecorder) From() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "From", reflect.TypeOf((*MockChainClient)(nil).From)) +} + +// LockNonce mocks base method. +func (m *MockChainClient) LockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "LockNonce") +} + +// LockNonce indicates an expected call of LockNonce. +func (mr *MockChainClientMockRecorder) LockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LockNonce", reflect.TypeOf((*MockChainClient)(nil).LockNonce)) +} + +// RelayerAddress mocks base method. +func (m *MockChainClient) RelayerAddress() common.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RelayerAddress") + ret0, _ := ret[0].(common.Address) + return ret0 +} + +// RelayerAddress indicates an expected call of RelayerAddress. +func (mr *MockChainClientMockRecorder) RelayerAddress() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RelayerAddress", reflect.TypeOf((*MockChainClient)(nil).RelayerAddress)) +} + +// SignAndSendTransaction mocks base method. +func (m *MockChainClient) SignAndSendTransaction(arg0 context.Context, arg1 evmclient.CommonTransaction) (common.Hash, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignAndSendTransaction", arg0, arg1) + ret0, _ := ret[0].(common.Hash) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignAndSendTransaction indicates an expected call of SignAndSendTransaction. +func (mr *MockChainClientMockRecorder) SignAndSendTransaction(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignAndSendTransaction", reflect.TypeOf((*MockChainClient)(nil).SignAndSendTransaction), arg0, arg1) +} + +// SubscribePendingTransactions mocks base method. +func (m *MockChainClient) SubscribePendingTransactions(arg0 context.Context, arg1 chan<- common.Hash) (*rpc.ClientSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubscribePendingTransactions", arg0, arg1) + ret0, _ := ret[0].(*rpc.ClientSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubscribePendingTransactions indicates an expected call of SubscribePendingTransactions. +func (mr *MockChainClientMockRecorder) SubscribePendingTransactions(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribePendingTransactions", reflect.TypeOf((*MockChainClient)(nil).SubscribePendingTransactions), arg0, arg1) +} + +// TransactionByHash mocks base method. +func (m *MockChainClient) TransactionByHash(arg0 context.Context, arg1 common.Hash) (*types.Transaction, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionByHash", arg0, arg1) + ret0, _ := ret[0].(*types.Transaction) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TransactionByHash indicates an expected call of TransactionByHash. +func (mr *MockChainClientMockRecorder) TransactionByHash(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByHash", reflect.TypeOf((*MockChainClient)(nil).TransactionByHash), arg0, arg1) +} + +// UnlockNonce mocks base method. +func (m *MockChainClient) UnlockNonce() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UnlockNonce") +} + +// UnlockNonce indicates an expected call of UnlockNonce. +func (mr *MockChainClientMockRecorder) UnlockNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlockNonce", reflect.TypeOf((*MockChainClient)(nil).UnlockNonce)) +} + +// UnsafeIncreaseNonce mocks base method. +func (m *MockChainClient) UnsafeIncreaseNonce() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeIncreaseNonce") + ret0, _ := ret[0].(error) + return ret0 +} + +// UnsafeIncreaseNonce indicates an expected call of UnsafeIncreaseNonce. +func (mr *MockChainClientMockRecorder) UnsafeIncreaseNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeIncreaseNonce", reflect.TypeOf((*MockChainClient)(nil).UnsafeIncreaseNonce)) +} + +// UnsafeNonce mocks base method. +func (m *MockChainClient) UnsafeNonce() (*big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnsafeNonce") + ret0, _ := ret[0].(*big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnsafeNonce indicates an expected call of UnsafeNonce. +func (mr *MockChainClientMockRecorder) UnsafeNonce() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnsafeNonce", reflect.TypeOf((*MockChainClient)(nil).UnsafeNonce)) +} + +// WaitAndReturnTxReceipt mocks base method. +func (m *MockChainClient) WaitAndReturnTxReceipt(arg0 common.Hash) (*types.Receipt, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitAndReturnTxReceipt", arg0) + ret0, _ := ret[0].(*types.Receipt) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitAndReturnTxReceipt indicates an expected call of WaitAndReturnTxReceipt. +func (mr *MockChainClientMockRecorder) WaitAndReturnTxReceipt(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitAndReturnTxReceipt", reflect.TypeOf((*MockChainClient)(nil).WaitAndReturnTxReceipt), arg0) +} + +// MockMessageHandler is a mock of MessageHandler interface. +type MockMessageHandler struct { + ctrl *gomock.Controller + recorder *MockMessageHandlerMockRecorder +} + +// MockMessageHandlerMockRecorder is the mock recorder for MockMessageHandler. +type MockMessageHandlerMockRecorder struct { + mock *MockMessageHandler +} + +// NewMockMessageHandler creates a new mock instance. +func NewMockMessageHandler(ctrl *gomock.Controller) *MockMessageHandler { + mock := &MockMessageHandler{ctrl: ctrl} + mock.recorder = &MockMessageHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMessageHandler) EXPECT() *MockMessageHandlerMockRecorder { + return m.recorder +} + +// HandleMessage mocks base method. +func (m *MockMessageHandler) HandleMessage(arg0 *message.Message) (*proposal.Proposal, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleMessage", arg0) + ret0, _ := ret[0].(*proposal.Proposal) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HandleMessage indicates an expected call of HandleMessage. +func (mr *MockMessageHandlerMockRecorder) HandleMessage(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockMessageHandler)(nil).HandleMessage), arg0) +} diff --git a/chains/evm/voter/proposal/proposal.go b/chains/evm/voter/proposal/proposal.go index e97a3513..fb43c308 100644 --- a/chains/evm/voter/proposal/proposal.go +++ b/chains/evm/voter/proposal/proposal.go @@ -31,3 +31,8 @@ type Proposal struct { func (p *Proposal) GetDataHash() common.Hash { return crypto.Keccak256Hash(append(p.HandlerAddress.Bytes(), p.Data...)) } + +// GetID constructs proposal unique identifier +func (p *Proposal) GetID() common.Hash { + return crypto.Keccak256Hash(append([]byte{p.Source}, byte(p.DepositNonce))) +} diff --git a/chains/evm/voter/voter.go b/chains/evm/voter/voter.go index 67d5c02e..c824ea13 100644 --- a/chains/evm/voter/voter.go +++ b/chains/evm/voter/voter.go @@ -7,19 +7,35 @@ import ( "context" "fmt" "math/big" + "math/rand" + "strings" + "time" "github.com/ChainSafe/chainbridge-core/chains/evm/calls" + "github.com/ChainSafe/chainbridge-core/chains/evm/calls/consts" "github.com/ChainSafe/chainbridge-core/chains/evm/voter/proposal" "github.com/ChainSafe/chainbridge-core/relayer/message" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + ethereumTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/rs/zerolog/log" ) +const ( + maxShouldVoteChecks = 40 + shouldVoteCheckPeriod = 15 +) + +var ( + Sleep = time.Sleep +) + type ChainClient interface { - LatestBlock() (*big.Int, error) RelayerAddress() common.Address CallContract(ctx context.Context, callArgs map[string]interface{}, blockNumber *big.Int) ([]byte, error) - ChainID(ctx context.Context) (*big.Int, error) + SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) + TransactionByHash(ctx context.Context, hash common.Hash) (tx *ethereumTypes.Transaction, isPending bool, err error) calls.ClientDispatcher } @@ -28,42 +44,171 @@ type MessageHandler interface { } type EVMVoter struct { - mh MessageHandler - client ChainClient - fabric calls.TxFabric - gasPriceClient calls.GasPricer + mh MessageHandler + client ChainClient + fabric calls.TxFabric + gasPriceClient calls.GasPricer + pendingProposalVotes map[common.Hash]uint8 +} + +// NewVoterWithSubscription creates EVMVoter with pending proposal subscription +// that listens to pending voteProposal transactions and avoids wasting gas +// on sending votes for transactions that will fail. +// Currently, officialy supported only by Geth nodes. +func NewVoterWithSubscription(mh MessageHandler, client ChainClient, fabric calls.TxFabric, gasPriceClient calls.GasPricer) (*EVMVoter, error) { + voter := &EVMVoter{ + mh: mh, + client: client, + fabric: fabric, + gasPriceClient: gasPriceClient, + pendingProposalVotes: make(map[common.Hash]uint8), + } + + ch := make(chan common.Hash) + _, err := client.SubscribePendingTransactions(context.TODO(), ch) + if err != nil { + return nil, err + } + go voter.trackProposalPendingVotes(ch) + + return voter, nil } +// NewVoter creates EVMVoter without pending proposal subscription. +// It is a fallback for nodes that don't support pending transaction subscription +// and will vote on proposals that already satisfy threshold thus wasting gas. func NewVoter(mh MessageHandler, client ChainClient, fabric calls.TxFabric, gasPriceClient calls.GasPricer) *EVMVoter { return &EVMVoter{ - mh: mh, - client: client, - fabric: fabric, - gasPriceClient: gasPriceClient, + mh: mh, + client: client, + fabric: fabric, + gasPriceClient: gasPriceClient, + pendingProposalVotes: make(map[common.Hash]uint8), } } +// VoteProposal checks if relayer already voted and is threshold +// satisfied and casts a vote if it isn't. func (v *EVMVoter) VoteProposal(m *message.Message) error { prop, err := v.mh.HandleMessage(m) if err != nil { return err } - ps, err := calls.ProposalStatus(v.client, prop) + + votedByTheRelayer, err := calls.IsProposalVotedBy(v.client, v.client.RelayerAddress(), prop) if err != nil { - return fmt.Errorf("error getting proposal: %+v status %w", prop, err) + return err } - votedByTheRelayer, err := calls.IsProposalVotedBy(v.client, v.client.RelayerAddress(), prop) + if votedByTheRelayer { + return nil + } + + shouldVote, err := v.shouldVoteForProposal(prop, 0) if err != nil { + log.Error().Err(err) return err } - // if this relayer had not voted for proposal and proposal is in Active or Inactive status - // we need to vote for it - if !votedByTheRelayer && (ps == message.ProposalStatusActive || ps == message.ProposalStatusInactive) { - hash, err := calls.VoteProposal(v.client, v.fabric, v.gasPriceClient, prop) - log.Debug().Str("hash", hash.String()).Uint64("nonce", prop.DepositNonce).Msgf("Voted") + + if !shouldVote { + log.Debug().Msgf("Proposal %+v already satisfies threshold", prop) + return nil + } + + hash, err := calls.VoteProposal(v.client, v.fabric, v.gasPriceClient, prop) + if err != nil { + return fmt.Errorf("voting failed. Err: %w", err) + } + + log.Debug().Str("hash", hash.String()).Uint64("nonce", prop.DepositNonce).Msgf("Voted") + return nil +} + +// shouldVoteForProposal checks if proposal already has threshold with pending +// proposal votes from other relayers. +// Only works properly in conjuction with NewVoterWithSubscription as without a subscription +// no pending txs would be received and pending vote count would be 0. +func (v *EVMVoter) shouldVoteForProposal(prop *proposal.Proposal, tries int) (bool, error) { + propID := prop.GetID() + defer delete(v.pendingProposalVotes, propID) + + // random delay to prevent all relayers checking for pending votes + // at the same time and all of them sending another tx + Sleep(time.Duration(rand.Intn(shouldVoteCheckPeriod)) * time.Second) + + ps, err := calls.ProposalStatus(v.client, prop) + if err != nil { + return false, err + } + + if ps.Status == message.ProposalStatusExecuted || ps.Status == message.ProposalStatusCanceled { + return false, nil + } + + threshold, err := calls.GetThreshold(v.client, &prop.BridgeAddress) + if err != nil { + return false, err + } + + if ps.YesVotesTotal+v.pendingProposalVotes[propID] >= threshold && tries < maxShouldVoteChecks { + // Wait until proposal status is finalized to prevent missing votes + // in case of dropped txs + tries++ + return v.shouldVoteForProposal(prop, tries) + } + + return true, nil +} + +// trackProposalPendingVotes tracks pending voteProposal txs from +// other relayers and increases count of pending votes in pendingProposalVotes map +// by proposal unique id. +func (v *EVMVoter) trackProposalPendingVotes(ch chan common.Hash) { + for msg := range ch { + txData, _, err := v.client.TransactionByHash(context.TODO(), msg) + if err != nil { + log.Error().Err(err) + continue + } + + a, err := abi.JSON(strings.NewReader(consts.BridgeABI)) + if err != nil { + log.Error().Err(err) + continue + } + + m, err := a.MethodById(txData.Data()[:4]) + if err != nil { + continue + } + + data, err := m.Inputs.UnpackValues(txData.Data()[4:]) if err != nil { - return fmt.Errorf("voting failed. Err: %w", err) + log.Error().Err(err) + continue + } + + if m.Name == "voteProposal" { + source := data[0].(uint8) + depositNonce := data[1].(uint64) + prop := proposal.Proposal{ + Source: source, + DepositNonce: depositNonce, + } + + go v.increaseProposalVoteCount(msg, prop.GetID()) } } - return nil +} + +// increaseProposalVoteCount increases pending proposal vote for target proposal +// and decreases it when transaction is mined. +func (v *EVMVoter) increaseProposalVoteCount(hash common.Hash, propID common.Hash) { + v.pendingProposalVotes[propID]++ + + _, err := v.client.WaitAndReturnTxReceipt(hash) + if err != nil { + log.Error().Err(err) + } + + v.pendingProposalVotes[propID]-- } diff --git a/chains/evm/voter/voter_test.go b/chains/evm/voter/voter_test.go new file mode 100644 index 00000000..aa9fc5fe --- /dev/null +++ b/chains/evm/voter/voter_test.go @@ -0,0 +1,129 @@ +package voter_test + +import ( + "encoding/hex" + "errors" + "testing" + "time" + + "github.com/ChainSafe/chainbridge-core/chains/evm/evmgaspricer" + "github.com/ChainSafe/chainbridge-core/chains/evm/evmtransaction" + "github.com/ChainSafe/chainbridge-core/chains/evm/voter" + mock_voter "github.com/ChainSafe/chainbridge-core/chains/evm/voter/mock" + "github.com/ChainSafe/chainbridge-core/chains/evm/voter/proposal" + "github.com/ChainSafe/chainbridge-core/relayer/message" + "github.com/ethereum/go-ethereum/common" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" +) + +var ( + proposalVotedResponse, _ = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000001") + proposalNotVotedResponse, _ = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") + executedProposalStatus, _ = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000001f") + inactiveProposalStatus, _ = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") +) + +type VoterTestSuite struct { + suite.Suite + voter *voter.EVMVoter + mockMessageHandler *mock_voter.MockMessageHandler + mockClient *mock_voter.MockChainClient +} + +func TestRunVoterTestSuite(t *testing.T) { + suite.Run(t, new(VoterTestSuite)) +} + +func (s *VoterTestSuite) SetupSuite() {} +func (s *VoterTestSuite) TearDownSuite() {} +func (s *VoterTestSuite) SetupTest() { + gomockController := gomock.NewController(s.T()) + s.mockMessageHandler = mock_voter.NewMockMessageHandler(gomockController) + s.mockClient = mock_voter.NewMockChainClient(gomockController) + s.voter = voter.NewVoter( + s.mockMessageHandler, + s.mockClient, + evmtransaction.NewTransaction, + &evmgaspricer.LondonGasPriceDeterminant{}, + ) + voter.Sleep = func(d time.Duration) {} +} +func (s *VoterTestSuite) TearDownTest() {} + +func (s *VoterTestSuite) TestVoteProposal_HandleMessageError() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(nil, errors.New("error")) + + err := s.voter.VoteProposal(&message.Message{}) + + s.NotNil(err) +} + +func (s *VoterTestSuite) TestVoteProposal_IsProposalVotedByError() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(&proposal.Proposal{ + Source: 0, + DepositNonce: 0, + }, nil) + s.mockClient.EXPECT().RelayerAddress().Return(common.Address{}) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, errors.New("error")) + + err := s.voter.VoteProposal(&message.Message{}) + + s.NotNil(err) +} + +func (s *VoterTestSuite) TestVoteProposal_ProposalAlreadyVoted() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(&proposal.Proposal{ + Source: 0, + DepositNonce: 0, + }, nil) + s.mockClient.EXPECT().RelayerAddress().Return(common.Address{}) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(proposalVotedResponse, nil) + + err := s.voter.VoteProposal(&message.Message{}) + + s.Nil(err) +} + +func (s *VoterTestSuite) TestVoteProposal_ProposalStatusFail() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(&proposal.Proposal{ + Source: 0, + DepositNonce: 0, + }, nil) + s.mockClient.EXPECT().RelayerAddress().Return(common.Address{}) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(proposalNotVotedResponse, nil) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, errors.New("error")) + + err := s.voter.VoteProposal(&message.Message{}) + + s.NotNil(err) +} + +func (s *VoterTestSuite) TestVoteProposal_ExecutedProposal() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(&proposal.Proposal{ + Source: 0, + DepositNonce: 0, + }, nil) + s.mockClient.EXPECT().RelayerAddress().Return(common.Address{}) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(proposalNotVotedResponse, nil) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(executedProposalStatus, nil) + + err := s.voter.VoteProposal(&message.Message{}) + + s.Nil(err) +} + +func (s *VoterTestSuite) TestVoteProposal_GetThresholdFail() { + s.mockMessageHandler.EXPECT().HandleMessage(gomock.Any()).Return(&proposal.Proposal{ + Source: 0, + DepositNonce: 0, + }, nil) + s.mockClient.EXPECT().RelayerAddress().Return(common.Address{}) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(proposalNotVotedResponse, nil) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return(inactiveProposalStatus, nil) + s.mockClient.EXPECT().CallContract(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, errors.New("error")) + + err := s.voter.VoteProposal(&message.Message{}) + + s.NotNil(err) +} diff --git a/e2e/evm-evm/eve-eve_test.go b/e2e/evm-evm/evm-evm_test.go similarity index 61% rename from e2e/evm-evm/eve-eve_test.go rename to e2e/evm-evm/evm-evm_test.go index a1255486..045f64a2 100644 --- a/e2e/evm-evm/eve-eve_test.go +++ b/e2e/evm-evm/evm-evm_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/suite" ) -const ETHEndpoint1 = "http://localhost:8545" -const ETHEndpoint2 = "http://localhost:8547" +const ETHEndpoint1 = "ws://localhost:8546" +const ETHEndpoint2 = "ws://localhost:8548" // Alice key is used by the relayer, Eve key is used as admin and depositter func TestRunE2ETests(t *testing.T) { - suite.Run(t, evm.SetupEVM2EVEMTestSuite(evmtransaction.NewTransaction, evmtransaction.NewTransaction, ETHEndpoint1, ETHEndpoint2, local.EveKp)) + suite.Run(t, evm.SetupEVM2EVMTestSuite(evmtransaction.NewTransaction, evmtransaction.NewTransaction, ETHEndpoint1, ETHEndpoint2, local.EveKp)) } diff --git a/e2e/evm-evm/example/app/app.go b/e2e/evm-evm/example/app/app.go index d77a98a5..a1948ff1 100644 --- a/e2e/evm-evm/example/app/app.go +++ b/e2e/evm-evm/example/app/app.go @@ -50,8 +50,11 @@ func Run() error { mh.RegisterMessageHandler(common.HexToAddress(evm1Cfg.SharedEVMConfig.Erc20Handler), voter.ERC20MessageHandler) mh.RegisterMessageHandler(common.HexToAddress(evm1Cfg.SharedEVMConfig.Erc721Handler), voter.ERC721MessageHandler) mh.RegisterMessageHandler(common.HexToAddress(evm1Cfg.SharedEVMConfig.GenericHandler), voter.GenericMessageHandler) - evmeVoter := voter.NewVoter(mh, evm1Client, evmtransaction.NewTransaction, evmgaspricer.NewLondonGasPriceClient(evm1Client, nil)) + evmeVoter, err := voter.NewVoterWithSubscription(mh, evm1Client, evmtransaction.NewTransaction, evmgaspricer.NewLondonGasPriceClient(evm1Client, nil)) + if err != nil { + panic(err) + } evm1Chain := evm.NewEVMChain(evm1Listener, evmeVoter, db, *evm1Cfg.SharedEVMConfig.GeneralChainConfig.Id, &evm1Cfg.SharedEVMConfig) ////EVM2 setup @@ -73,8 +76,11 @@ func Run() error { mhEVM.RegisterMessageHandler(common.HexToAddress(evm2Config.SharedEVMConfig.Erc20Handler), voter.ERC20MessageHandler) mhEVM.RegisterMessageHandler(common.HexToAddress(evm2Config.SharedEVMConfig.Erc721Handler), voter.ERC721MessageHandler) mhEVM.RegisterMessageHandler(common.HexToAddress(evm2Config.SharedEVMConfig.GenericHandler), voter.GenericMessageHandler) - evm2Voter := voter.NewVoter(mhEVM, evm2Client, evmtransaction.NewTransaction, evmgaspricer.NewLondonGasPriceClient(evm2Client, nil)) + evm2Voter, err := voter.NewVoterWithSubscription(mhEVM, evm2Client, evmtransaction.NewTransaction, evmgaspricer.NewLondonGasPriceClient(evm2Client, nil)) + if err != nil { + panic(err) + } evm2Chain := evm.NewEVMChain(evm2Listener, evm2Voter, db, *evm2Config.SharedEVMConfig.GeneralChainConfig.Id, &evm2Config.SharedEVMConfig) r := relayer.NewRelayer([]relayer.RelayedChain{evm2Chain, evm1Chain}, &opentelemetry.ConsoleTelemetry{}) diff --git a/e2e/evm-evm/example/cfg/config_evm1.json b/e2e/evm-evm/example/cfg/config_evm1.json index 3780b7df..cf6383f4 100644 --- a/e2e/evm-evm/example/cfg/config_evm1.json +++ b/e2e/evm-evm/example/cfg/config_evm1.json @@ -1,7 +1,7 @@ { "name": "evm1", "id": "1", - "endpoint": "http://evm1-1:8545", + "endpoint": "ws://evm1-1:8546", "from": "0xff93B45308FD417dF303D6515aB04D9e89a750Ca", "bridge": "0xd606A00c1A39dA53EA7Bb3Ab570BBE40b156EB66", "erc20Handler": "0x05C5AFACf64A6082D4933752FfB447AED63581b1", diff --git a/e2e/evm-evm/example/cfg/config_evm2.json b/e2e/evm-evm/example/cfg/config_evm2.json index 9bf3bbe4..6878e6fb 100644 --- a/e2e/evm-evm/example/cfg/config_evm2.json +++ b/e2e/evm-evm/example/cfg/config_evm2.json @@ -1,7 +1,7 @@ { "name": "evm2", "id": "2", - "endpoint": "http://evm2-1:8545", + "endpoint": "ws://evm2-1:8546", "from": "0xff93B45308FD417dF303D6515aB04D9e89a750Ca", "bridge": "0xd606A00c1A39dA53EA7Bb3Ab570BBE40b156EB66", "erc20Handler": "0x05C5AFACf64A6082D4933752FfB447AED63581b1", diff --git a/e2e/evm/test.go b/e2e/evm/test.go index 18cfec07..f3722620 100644 --- a/e2e/evm/test.go +++ b/e2e/evm/test.go @@ -25,7 +25,7 @@ type TestClient interface { FetchEventLogs(ctx context.Context, contractAddress common.Address, event string, startBlock *big.Int, endBlock *big.Int) ([]types.Log, error) } -func SetupEVM2EVEMTestSuite(fabric1, fabric2 calls.TxFabric, endpoint1, endpoint2 string, adminKey *secp256k1.Keypair) *IntegrationTestSuite { +func SetupEVM2EVMTestSuite(fabric1, fabric2 calls.TxFabric, endpoint1, endpoint2 string, adminKey *secp256k1.Keypair) *IntegrationTestSuite { return &IntegrationTestSuite{ fabric1: fabric1, fabric2: fabric2, diff --git a/go.mod b/go.mod index 2cfd4fc3..aed46343 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/centrifuge/go-substrate-rpc-client v2.0.0+incompatible - github.com/ethereum/go-ethereum v1.10.9 + github.com/ethereum/go-ethereum v1.10.12 github.com/golang/mock v1.6.0 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.25.0 @@ -36,8 +36,11 @@ require ( github.com/google/uuid v1.1.5 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huin/goupnp v1.0.2 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect @@ -51,12 +54,13 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - go.opentelemetry.io/otel v1.1.0 // indirect + go.opentelemetry.io/otel v1.0.1 // indirect go.opentelemetry.io/otel/internal/metric v0.24.0 // indirect - go.opentelemetry.io/otel/sdk v1.1.0 // indirect - go.opentelemetry.io/otel/trace v1.1.0 // indirect + go.opentelemetry.io/otel/sdk v1.0.1 // indirect + go.opentelemetry.io/otel/trace v1.0.1 // indirect go.opentelemetry.io/proto/otlp v0.9.0 // indirect golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect diff --git a/go.sum b/go.sum index 70ef9ac8..942b8130 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -154,9 +155,11 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= @@ -172,8 +175,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo= -github.com/ethereum/go-ethereum v1.10.9 h1:uMSWt0qDhaqqCk0PWqfDFOMUExmk4Tnbma6c6oXW+Pk= -github.com/ethereum/go-ethereum v1.10.9/go.mod h1:CaTMQrv51WaAlD2eULQ3f03KiahDRO28fleQcKjWrrg= +github.com/ethereum/go-ethereum v1.10.12 h1:el/KddB3gLEsnNgGQ3SQuZuiZjwnFTYHe5TwUet5Om4= +github.com/ethereum/go-ethereum v1.10.12/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -204,6 +207,7 @@ github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dT github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -383,6 +387,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -395,11 +400,13 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -608,9 +615,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc= go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= -go.opentelemetry.io/otel v1.1.0 h1:8p0uMLcyyIx0KHNTgO8o3CW8A1aA+dJZJW6PvnMz0Wc= -go.opentelemetry.io/otel v1.1.0/go.mod h1:7cww0OW51jQ8IaZChIEdqLwgh+44+7uiTdWsAL0wQpA= go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.24.0 h1:NN6n2agAkT6j2o+1RPTFANclOnZ/3Z1ruRGL06NYACk= go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.24.0/go.mod h1:kgWmavsno59/h5l9A9KXhvqrYxBhiQvJHPNhJkMP46s= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.24.0 h1:y7JFNNVfC/CWN/eoIJfJJyi0B79bKnpvUoBk24BME6g= @@ -619,16 +625,14 @@ go.opentelemetry.io/otel/internal/metric v0.24.0 h1:O5lFy6kAl0LMWBjzy3k//M8VjEaT go.opentelemetry.io/otel/internal/metric v0.24.0/go.mod h1:PSkQG+KuApZjBpC6ea6082ZrWUUy/w132tJ/LOU3TXk= go.opentelemetry.io/otel/metric v0.24.0 h1:Rg4UYHS6JKR1Sw1TxnI13z7q/0p/XAbgIqUTagvLJuU= go.opentelemetry.io/otel/metric v0.24.0/go.mod h1:tpMFnCD9t+BEGiWY2bWF5+AwjuAdM0lSowQ4SBA3/K4= +go.opentelemetry.io/otel/sdk v1.0.1 h1:wXxFEWGo7XfXupPwVJvTBOaPBC9FEg0wB8hMNrKk+cA= go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= -go.opentelemetry.io/otel/sdk v1.1.0 h1:j/1PngUJIDOddkCILQYTevrTIbWd494djgGkSsMit+U= -go.opentelemetry.io/otel/sdk v1.1.0/go.mod h1:3aQvM6uLm6C4wJpHtT8Od3vNzeZ34Pqc6bps8MywWzo= go.opentelemetry.io/otel/sdk/export/metric v0.24.0 h1:innKi8LQebwPI+WEuEKEWMjhWC5mXQG1/WpSm5mffSY= go.opentelemetry.io/otel/sdk/export/metric v0.24.0/go.mod h1:chmxXGVNcpCih5XyniVkL4VUyaEroUbOdvjVlQ8M29Y= go.opentelemetry.io/otel/sdk/metric v0.24.0 h1:LLHrZikGdEHoHihwIPvfFRJX+T+NdrU2zgEqf7tQ7Oo= go.opentelemetry.io/otel/sdk/metric v0.24.0/go.mod h1:KDgJgYzsIowuIDbPM9sLDZY9JJ6gqIDWCx92iWV8ejk= +go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw= go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= -go.opentelemetry.io/otel/trace v1.1.0 h1:N25T9qCL0+7IpOT8RrRy0WYlL7y6U0WiUJzXcVdXY/o= -go.opentelemetry.io/otel/trace v1.1.0/go.mod h1:i47XtdcBQiktu5IsrPqOHe8w+sBmnLwwHt8wiUsWGTI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= @@ -1069,8 +1073,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/relayer/message/message.go b/relayer/message/message.go index a78aaf1b..faba1e71 100644 --- a/relayer/message/message.go +++ b/relayer/message/message.go @@ -18,10 +18,15 @@ const ( GenericTransfer TransferType = "GenericTransfer" ) -type ProposalStatus uint8 +type ProposalStatus struct { + Status uint8 + YesVotes *big.Int + YesVotesTotal uint8 + ProposedBlock *big.Int +} const ( - ProposalStatusInactive ProposalStatus = iota + ProposalStatusInactive uint8 = iota ProposalStatusActive ProposalStatusPassed // Ready to be executed ProposalStatusExecuted @@ -29,7 +34,7 @@ const ( ) var ( - StatusMap = map[ProposalStatus]string{ProposalStatusInactive: "inactive", ProposalStatusActive: "active", ProposalStatusPassed: "passed", ProposalStatusExecuted: "executed", ProposalStatusCanceled: "canceled"} + StatusMap = map[uint8]string{ProposalStatusInactive: "inactive", ProposalStatusActive: "active", ProposalStatusPassed: "passed", ProposalStatusExecuted: "executed", ProposalStatusCanceled: "canceled"} ) type Message struct {