diff --git a/cosmos/signer.service.go b/cosmos/signer.service.go index aadda14..df3a552 100644 --- a/cosmos/signer.service.go +++ b/cosmos/signer.service.go @@ -741,6 +741,7 @@ func (x *CosmosMessageSignerRunnable) ValidateCosmosTxAndBroadcastRefund( logger.WithError(err).Error("Error locking refund") return false } else { + //nolint:errcheck defer x.db.Unlock(lockID) } @@ -821,6 +822,7 @@ func (x *CosmosMessageSignerRunnable) ValidateCosmosTxAndSignRefund( logger.WithError(err).Error("Error locking refund") return false } else { + //nolint:errcheck defer x.db.Unlock(lockID) } diff --git a/cosmos/signer_test.go b/cosmos/signer_test.go index 47041e1..ce17c9a 100644 --- a/cosmos/signer_test.go +++ b/cosmos/signer_test.go @@ -2260,7 +2260,8 @@ func TestValidateSignatures_Threshold(t *testing.T) { sigV2, msg, err := util.SignWithPrivKey(context.Background(), signerData, txBuilder, signerKey, txConfig, 1) assert.NoError(t, err) - txBuilder.SetSignatures(sigV2) + err = txBuilder.SetSignatures(sigV2) + assert.NoError(t, err) sigs, err := txBuilder.GetTx().GetSignaturesV2() assert.NoError(t, err) @@ -2297,7 +2298,8 @@ func TestValidateSignatures_Threshold(t *testing.T) { multisigSig := multisigtypes.NewMultisig(len(multisigPk.PubKeys)) - multisigtypes.AddSignatureV2(multisigSig, sigV2, multisigPk.GetPubKeys()) + err = multisigtypes.AddSignatureV2(multisigSig, sigV2, multisigPk.GetPubKeys()) + assert.NoError(t, err) expectedSignature := signingtypes.SignatureV2{ PubKey: multisigPk, @@ -2357,7 +2359,8 @@ func TestValidateSignatures_Threshold_AnyError(t *testing.T) { sigV2.PubKey = nil - txBuilder.SetSignatures(sigV2) + err = txBuilder.SetSignatures(sigV2) + assert.NoError(t, err) result := signer.ValidateSignaturesAndAddMultiSignatureToTxConfig("hash1", 1, txConfig, txBuilder) assert.False(t, result) @@ -2404,7 +2407,8 @@ func TestValidateSignatures_Threshold_VerifyError(t *testing.T) { sigV2, _, err := util.SignWithPrivKey(context.Background(), signerData, txBuilder, signerKey, txConfig, 1) assert.NoError(t, err) - txBuilder.SetSignatures(sigV2) + err = txBuilder.SetSignatures(sigV2) + assert.NoError(t, err) result := signer.ValidateSignaturesAndAddMultiSignatureToTxConfig("hash1", 1, txConfig, txBuilder) assert.False(t, result) @@ -2452,7 +2456,8 @@ func TestValidateSignatures_Threshold_AddSignatureError(t *testing.T) { sigV2, _, err := util.SignWithPrivKey(context.Background(), signerData, txBuilder, signerKey, txConfig, 1) assert.NoError(t, err) - txBuilder.SetSignatures(sigV2) + err = txBuilder.SetSignatures(sigV2) + assert.NoError(t, err) result := signer.ValidateSignaturesAndAddMultiSignatureToTxConfig("hash1", 1, txConfig, txBuilder) assert.False(t, result) @@ -2540,7 +2545,8 @@ func TestValidateSignatures_TwoThreshold(t *testing.T) { sig3, _, err := util.SignWithPrivKey(context.Background(), signerData, txBuilder, signer3Key, txConfig, 1) assert.NoError(t, err) - txBuilder.SetSignatures(sig1, sig3) + err = txBuilder.SetSignatures(sig1, sig3) + assert.NoError(t, err) sigs, err := txBuilder.GetTx().GetSignaturesV2() assert.NoError(t, err) @@ -2550,8 +2556,10 @@ func TestValidateSignatures_TwoThreshold(t *testing.T) { multisigSig := multisigtypes.NewMultisig(len(multisigPk.PubKeys)) - multisigtypes.AddSignatureV2(multisigSig, sig1, multisigPk.GetPubKeys()) - multisigtypes.AddSignatureV2(multisigSig, sig3, multisigPk.GetPubKeys()) + err = multisigtypes.AddSignatureV2(multisigSig, sig1, multisigPk.GetPubKeys()) + assert.NoError(t, err) + err = multisigtypes.AddSignatureV2(multisigSig, sig3, multisigPk.GetPubKeys()) + assert.NoError(t, err) expectedSignature := signingtypes.SignatureV2{ PubKey: multisigPk, diff --git a/ethereum/monitor.service.go b/ethereum/monitor.service.go index 260f850..62c9457 100644 --- a/ethereum/monitor.service.go +++ b/ethereum/monitor.service.go @@ -428,6 +428,10 @@ func (x *EthMessageMonitorRunnable) InitStartBlockHeight(lastHealth *models.Runn x.logger.Infof("Initialized start block height: %d", x.startBlockHeight) } +var ethNewClient = eth.NewClient +var ethNewMailboxContract = eth.NewMailboxContract +var dbNewDB = db.NewDB + func NewMessageMonitor( config models.EthereumNetworkConfig, mintControllerMap map[uint32][]byte, @@ -445,13 +449,13 @@ func NewMessageMonitor( logger.Debugf("Initializing") - client, err := eth.NewClient(config) + client, err := ethNewClient(config) if err != nil { logger.Fatalf("Error creating ethereum client: %s", err) } logger.Debug("Connecting to mailbox contract at: ", config.MailboxAddress) - mailbox, err := eth.NewMailboxContract(common.HexToAddress(config.MailboxAddress), client.GetClient()) + mailbox, err := ethNewMailboxContract(common.HexToAddress(config.MailboxAddress), client.GetClient()) if err != nil { logger.Fatal("Error connecting to mailbox contract: ", err) } @@ -472,7 +476,7 @@ func NewMessageMonitor( logger: logger, - db: db.NewDB(), + db: dbNewDB(), } x.UpdateCurrentBlockHeight() diff --git a/ethereum/monitor_test.go b/ethereum/monitor_test.go index c216992..b67d694 100644 --- a/ethereum/monitor_test.go +++ b/ethereum/monitor_test.go @@ -2,10 +2,12 @@ package ethereum import ( "context" + "fmt" "math/big" "testing" "github.com/dan13ram/wpokt-oracle/common" + "github.com/dan13ram/wpokt-oracle/db" "github.com/dan13ram/wpokt-oracle/db/mocks" "github.com/dan13ram/wpokt-oracle/ethereum/autogen" eth "github.com/dan13ram/wpokt-oracle/ethereum/client" @@ -1550,6 +1552,37 @@ func TestMonitorSyncBlocks_Error(t *testing.T) { assert.False(t, result) } +func TestMonitorSyncNewBlocks_NoNewBlocks(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + logger := log.New().WithField("test", "monitor") + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + } + + startBlock := uint64(100) + endBlock := uint64(100) + + mailbox := clientMocks.NewMockMailboxContract(t) + monitor := &EthMessageMonitorRunnable{ + db: mockDB, + client: mockClient, + logger: logger, + mintControllerMap: mintControllerMap, + chain: models.Chain{ + ChainDomain: 1, + }, + mailbox: mailbox, + currentBlockHeight: endBlock, + startBlockHeight: startBlock, + } + + result := monitor.SyncNewBlocks() + + assert.True(t, result) +} + func TestMonitorSyncNewBlocks(t *testing.T) { mockDB := mocks.NewMockDB(t) mockClient := clientMocks.NewMockEthereumClient(t) @@ -1826,3 +1859,153 @@ func TestMonitorRun(t *testing.T) { mockClient.AssertExpectations(t) mockDB.AssertExpectations(t) } + +func TestNewMessageMonitor(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMailbox := clientMocks.NewMockMailboxContract(t) + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + 2: ethcommon.FromHex("0x02"), + } + + lastHealth := &models.RunnerServiceStatus{BlockHeight: 50} + + config := models.EthereumNetworkConfig{ + ChainID: 1, + ChainName: "test", + StartBlockHeight: 1, + Confirmations: 10, + MailboxAddress: ethcommon.BytesToAddress([]byte("mailbox")).Hex(), + MessageMonitor: models.ServiceConfig{ + Enabled: true, + }, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) + + mockClient.EXPECT().GetClient().Return(nil) + + runnable := NewMessageMonitor(config, mintControllerMap, lastHealth) + + assert.NotNil(t, runnable) + + monitor, ok := runnable.(*EthMessageMonitorRunnable) + assert.True(t, ok) + assert.Equal(t, mockDB, monitor.db) + assert.Equal(t, mockClient, monitor.client) + assert.Equal(t, mockMailbox, monitor.mailbox) + assert.Equal(t, mintControllerMap, monitor.mintControllerMap) + assert.Equal(t, uint64(100), monitor.currentBlockHeight) + assert.Equal(t, uint64(50), monitor.startBlockHeight) + +} + +func TestNewMessageMonitorFailures(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMailbox := clientMocks.NewMockMailboxContract(t) + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + 2: ethcommon.FromHex("0x02"), + } + + lastHealth := &models.RunnerServiceStatus{BlockHeight: 50} + + config := models.EthereumNetworkConfig{ + ChainID: 1, + ChainName: "test", + StartBlockHeight: 1, + Confirmations: 10, + MailboxAddress: ethcommon.BytesToAddress([]byte("mailbox")).Hex(), + MessageMonitor: models.ServiceConfig{ + Enabled: true, + }, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetClient().Return(nil) + + defer func() { log.StandardLogger().ExitFunc = nil }() + log.StandardLogger().ExitFunc = func(num int) { panic(fmt.Sprintf("exit %d", num)) } + + t.Run("Disabled", func(t *testing.T) { + config.MessageMonitor.Enabled = false + + assert.Panics(t, func() { + NewMessageMonitor(config, mintControllerMap, lastHealth) + }) + + config.MessageMonitor.Enabled = true + }) + + t.Run("ClientError", func(t *testing.T) { + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageMonitor(config, mintControllerMap, lastHealth) + }) + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + }) + + t.Run("MailboxError", func(t *testing.T) { + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageMonitor(config, mintControllerMap, lastHealth) + }) + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + }) + +} diff --git a/ethereum/relayer.service.go b/ethereum/relayer.service.go index 3f58d48..f6efb0b 100644 --- a/ethereum/relayer.service.go +++ b/ethereum/relayer.service.go @@ -359,13 +359,13 @@ func NewMessageRelayer( logger.Debugf("Initializing") - client, err := eth.NewClient(config) + client, err := ethNewClient(config) if err != nil { logger.Fatalf("Error creating ethereum client: %s", err) } logger.Debug("Connecting to mintController contract at: ", config.MintControllerAddress) - mintController, err := eth.NewMintControllerContract(common.HexToAddress(config.MintControllerAddress), client.GetClient()) + mintController, err := ethNewMintControllerContract(common.HexToAddress(config.MintControllerAddress), client.GetClient()) if err != nil { logger.Fatal("Error connecting to mintController contract: ", err) } @@ -386,7 +386,7 @@ func NewMessageRelayer( logger: logger, - db: db.NewDB(), + db: dbNewDB(), } x.UpdateCurrentBlockHeight() diff --git a/ethereum/relayer_test.go b/ethereum/relayer_test.go index 73fa87a..9ea29fc 100644 --- a/ethereum/relayer_test.go +++ b/ethereum/relayer_test.go @@ -2,17 +2,20 @@ package ethereum import ( "context" + "fmt" "math/big" "testing" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "github.com/dan13ram/wpokt-oracle/db" "github.com/dan13ram/wpokt-oracle/db/mocks" "github.com/dan13ram/wpokt-oracle/ethereum/autogen" eth "github.com/dan13ram/wpokt-oracle/ethereum/client" @@ -1303,3 +1306,153 @@ func TestRelayerRun(t *testing.T) { relayer.Run() } + +func TestNewMessageRelayer(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMintController := clientMocks.NewMockMintControllerContract(t) + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + 2: ethcommon.FromHex("0x02"), + } + + lastHealth := &models.RunnerServiceStatus{BlockHeight: 50} + + config := models.EthereumNetworkConfig{ + ChainID: 1, + ChainName: "test", + StartBlockHeight: 1, + Confirmations: 10, + MintControllerAddress: ethcommon.BytesToAddress([]byte("mintController")).Hex(), + MessageRelayer: models.ServiceConfig{ + Enabled: true, + }, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMintControllerContract = eth.NewMintControllerContract + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) + + mockClient.EXPECT().GetClient().Return(nil) + + runnable := NewMessageRelayer(config, mintControllerMap, lastHealth) + + assert.NotNil(t, runnable) + + monitor, ok := runnable.(*EthMessageRelayerRunnable) + assert.True(t, ok) + assert.Equal(t, mockDB, monitor.db) + assert.Equal(t, mockClient, monitor.client) + assert.Equal(t, mockMintController, monitor.mintController) + assert.Equal(t, mintControllerMap, monitor.mintControllerMap) + assert.Equal(t, uint64(100), monitor.currentBlockHeight) + assert.Equal(t, uint64(50), monitor.startBlockHeight) + +} + +func TestNewMessageRelayerFailures(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMintController := clientMocks.NewMockMintControllerContract(t) + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + 2: ethcommon.FromHex("0x02"), + } + + lastHealth := &models.RunnerServiceStatus{BlockHeight: 50} + + config := models.EthereumNetworkConfig{ + ChainID: 1, + ChainName: "test", + StartBlockHeight: 1, + Confirmations: 10, + MintControllerAddress: ethcommon.BytesToAddress([]byte("mintController")).Hex(), + MessageRelayer: models.ServiceConfig{ + Enabled: true, + }, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetClient().Return(nil) + + defer func() { log.StandardLogger().ExitFunc = nil }() + log.StandardLogger().ExitFunc = func(num int) { panic(fmt.Sprintf("exit %d", num)) } + + t.Run("Disabled", func(t *testing.T) { + config.MessageRelayer.Enabled = false + + assert.Panics(t, func() { + NewMessageRelayer(config, mintControllerMap, lastHealth) + }) + + config.MessageRelayer.Enabled = true + }) + + t.Run("ClientError", func(t *testing.T) { + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageRelayer(config, mintControllerMap, lastHealth) + }) + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + }) + + t.Run("MintControllerError", func(t *testing.T) { + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageRelayer(config, mintControllerMap, lastHealth) + }) + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + }) + +} diff --git a/ethereum/service.go b/ethereum/service.go index d650974..f9969a7 100644 --- a/ethereum/service.go +++ b/ethereum/service.go @@ -29,7 +29,7 @@ func NewEthereumChainService( var chainHealth models.ChainServiceHealth if nodeHealth != nil { for _, health := range nodeHealth.Health { - if health.Chain.ChainID == fmt.Sprintf("%d", config.ChainID) && health.Chain.ChainType == models.ChainTypeCosmos { + if health.Chain.ChainID == fmt.Sprintf("%d", config.ChainID) && health.Chain.ChainType == models.ChainTypeEthereum { chainHealth = health break } diff --git a/ethereum/service_test.go b/ethereum/service_test.go new file mode 100644 index 0000000..770c3f1 --- /dev/null +++ b/ethereum/service_test.go @@ -0,0 +1,190 @@ +package ethereum + +import ( + "fmt" + "math/big" + "sync" + "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + cosmos "github.com/dan13ram/wpokt-oracle/cosmos/client" + cosmosMocks "github.com/dan13ram/wpokt-oracle/cosmos/client/mocks" + "github.com/dan13ram/wpokt-oracle/db" + "github.com/dan13ram/wpokt-oracle/db/mocks" + clientMocks "github.com/dan13ram/wpokt-oracle/ethereum/client/mocks" + "github.com/dan13ram/wpokt-oracle/ethereum/util" + "github.com/dan13ram/wpokt-oracle/models" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + + eth "github.com/dan13ram/wpokt-oracle/ethereum/client" +) + +func TestNewEthereumService(t *testing.T) { + defer func() { log.StandardLogger().ExitFunc = nil }() + log.StandardLogger().ExitFunc = func(num int) { panic(fmt.Sprintf("exit %d", num)) } + + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMailbox := clientMocks.NewMockMailboxContract(t) + mockMintController := clientMocks.NewMockMintControllerContract(t) + mockWarpISM := clientMocks.NewMockWarpISMContract(t) + mockCosmosClient := cosmosMocks.NewMockCosmosClient(t) + + mnemonic := "infant apart enroll relief kangaroo patch awesome wagon trap feature armor approve" + + config := models.EthereumNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:8545", + TimeoutMS: 1000, + ChainID: 1, + ChainName: "Ethereum", + MailboxAddress: "0x0000000000000000000000000000000000000000", + MintControllerAddress: "0x0000000000000000000000000000000000000000", + OmniTokenAddress: "0x0000000000000000000000000000000000000000", + WarpISMAddress: "0x0000000000000000000000000000000000000000", + OracleAddresses: []string{"0x0E90A32Df6f6143F1A91c25d9552dCbc789C34Eb", "0x958d1F55E14Cba24a077b9634F16f83565fc9411", "0x4c672Edd2ec8eac8f0F1709f33de9A2E786e6912"}, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + cosmosNetwork := models.CosmosNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:36657", + GRPCEnabled: true, + GRPCHost: "localhost", + GRPCPort: 9090, + TimeoutMS: 1000, + ChainID: "poktroll", + ChainName: "Poktroll", + TxFee: 1000, + Bech32Prefix: "pokt", + CoinDenom: "upokt", + MultisigAddress: "pokt13tsl3aglfyzf02n7x28x2ajzw94muu6y57k2ar", + MultisigPublicKeys: []string{"026892de2ec7fdf3125bc1bfd2ff2590d2c9ba756f98a05e9e843ac4d2a1acd4d9", "02faaaf0f385bb17381f36dcd86ab2486e8ff8d93440436496665ac007953076c2", "02cae233806460db75a941a269490ca5165a620b43241edb8bc72e169f4143a6df"}, + MultisigThreshold: 2, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + ethNetworks := []models.EthereumNetworkConfig{ + config, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + ethNewWarpISMContract = func(ethcommon.Address, bind.ContractBackend) (eth.WarpISMContract, error) { + return mockWarpISM, nil + } + + cosmosNewClient = func(models.CosmosNetworkConfig) (cosmos.CosmosClient, error) { + return mockCosmosClient, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + ethNewMintControllerContract = eth.NewMintControllerContract + ethNewWarpISMContract = eth.NewWarpISMContract + cosmosNewClient = cosmos.NewClient + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) + + mockCosmosClient.EXPECT().GetLatestBlockHeight().Return(int64(100), nil) + + mockClient.EXPECT().GetClient().Return(nil) + + mockClient.EXPECT().Chain().Return(models.Chain{ChainDomain: uint32(1)}) + + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(3), nil) + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(2), nil) + mockWarpISM.EXPECT().Eip712Domain(mock.Anything).Return(util.DomainData{ChainId: big.NewInt(1), VerifyingContract: ethcommon.HexToAddress(config.WarpISMAddress)}, nil) + mockMintController.EXPECT().MaxMintLimit(mock.Anything).Return(big.NewInt(100), nil) + + mintControllerMap := map[uint32][]byte{ + 1: ethcommon.FromHex("0x01"), + 2: ethcommon.FromHex("0x02"), + } + + assert.Panics(t, func() { + NewEthereumChainService( + config, + cosmosNetwork, + mintControllerMap, + ethNetworks, + mnemonic, + nil, + nil, + ) + }) + + wg := &sync.WaitGroup{} + lastHealth := models.ChainServiceHealth{ + Chain: models.Chain{ + ChainID: "1", + ChainDomain: 1, + ChainName: "Ethereum", + ChainType: models.ChainTypeEthereum, + }, + MessageMonitor: &models.RunnerServiceStatus{BlockHeight: 100}, + MessageSigner: &models.RunnerServiceStatus{BlockHeight: 100}, + MessageRelayer: &models.RunnerServiceStatus{BlockHeight: 100}, + } + + node := &models.Node{ + Health: []models.ChainServiceHealth{lastHealth}, + } + + service := NewEthereumChainService( + config, + cosmosNetwork, + mintControllerMap, + ethNetworks, + mnemonic, + wg, + node, + ) + assert.NotNil(t, service) + +} diff --git a/ethereum/signer.service.go b/ethereum/signer.service.go index 8bc8fbd..1530674 100644 --- a/ethereum/signer.service.go +++ b/ethereum/signer.service.go @@ -59,7 +59,6 @@ type EthMessageSignerRunnable struct { func (x *EthMessageSignerRunnable) Run() { x.UpdateCurrentBlockHeight() - x.UpdateMaxMintLimit() x.SignMessages() } @@ -377,6 +376,10 @@ func (x *EthMessageSignerRunnable) UpdateMaxMintLimit() { x.maximumAmount = mintLimit } +var ethNewMintControllerContract = eth.NewMintControllerContract +var ethNewWarpISMContract = eth.NewWarpISMContract +var cosmosNewClient = cosmos.NewClient + func NewMessageSigner( mnemonic string, config models.EthereumNetworkConfig, @@ -395,20 +398,20 @@ func NewMessageSigner( logger.Debugf("Initializing") - client, err := eth.NewClient(config) + client, err := ethNewClient(config) if err != nil { logger.Fatalf("Error creating ethereum client: %s", err) } logger.Debug("Connecting to mint controller contract at: ", config.MintControllerAddress) - mintController, err := eth.NewMintControllerContract(common.HexToAddress(config.MintControllerAddress), client.GetClient()) + mintController, err := ethNewMintControllerContract(common.HexToAddress(config.MintControllerAddress), client.GetClient()) if err != nil { logger.Fatal("Error connecting to mint controller contract: ", err) } logger.Debug("Connected to mint controller contract") logger.Debug("Connecting to warp ism contract at: ", config.WarpISMAddress) - warpISM, err := eth.NewWarpISMContract(common.HexToAddress(config.WarpISMAddress), client.GetClient()) + warpISM, err := ethNewWarpISMContract(common.HexToAddress(config.WarpISMAddress), client.GetClient()) if err != nil { logger.Fatal("Error connecting to warp ism contract: ", err) } @@ -418,11 +421,8 @@ func NewMessageSigner( if err != nil { logger.Fatalf("Error getting private key from mnemonic: %s", err) } - if privateKey == nil { - logger.Fatalf("Private key is nil") - } - cosmosClient, err := cosmos.NewClient(cosmosConfig) + cosmosClient, err := cosmosNewClient(cosmosConfig) if err != nil { logger.Fatalf("Error creating cosmos client: %s", err) } @@ -436,13 +436,13 @@ func NewMessageSigner( if ethConfig.ChainID == config.ChainID { ethClient = client } else { - ethClient, err = eth.NewClient(ethConfig) + ethClient, err = ethNewClient(ethConfig) if err != nil { logger.WithError(err).WithField("eth_chain_id", ethConfig.ChainID). Fatalf("Error creating ethereum client") } } - mailbox, err := eth.NewMailboxContract(common.HexToAddress(ethConfig.MailboxAddress), ethClient.GetClient()) + mailbox, err := ethNewMailboxContract(common.HexToAddress(ethConfig.MailboxAddress), ethClient.GetClient()) if err != nil { logger.WithError(err).WithField("eth_chain_id", ethConfig.ChainID). Fatalf("Error creating mailbox contract") @@ -477,7 +477,7 @@ func NewMessageSigner( logger: logger, - db: db.NewDB(), + db: dbNewDB(), } x.UpdateCurrentBlockHeight() diff --git a/ethereum/signer_test.go b/ethereum/signer_test.go index 88b8429..4f28204 100644 --- a/ethereum/signer_test.go +++ b/ethereum/signer_test.go @@ -2,6 +2,7 @@ package ethereum import ( "crypto/ecdsa" + "fmt" "math/big" "testing" "time" @@ -11,12 +12,15 @@ import ( "github.com/stretchr/testify/mock" "go.mongodb.org/mongo-driver/bson/primitive" + cosmos "github.com/dan13ram/wpokt-oracle/cosmos/client" cosmosMocks "github.com/dan13ram/wpokt-oracle/cosmos/client/mocks" cosmosUtil "github.com/dan13ram/wpokt-oracle/cosmos/util" + "github.com/dan13ram/wpokt-oracle/db" "github.com/dan13ram/wpokt-oracle/db/mocks" clientMocks "github.com/dan13ram/wpokt-oracle/ethereum/client/mocks" "github.com/dan13ram/wpokt-oracle/ethereum/util" "github.com/dan13ram/wpokt-oracle/models" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -1964,10 +1968,416 @@ func TestSignerRun(t *testing.T) { mockEthClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) mockCosmosClient.EXPECT().GetLatestBlockHeight().Return(int64(200), nil) - mintController.EXPECT().MaxMintLimit(mock.Anything).Return(big.NewInt(100), nil) mockDB.EXPECT().GetPendingMessages(mock.Anything, mock.Anything).Return([]models.Message{}, nil) signer.Run() +} + +func TestNewMessageSigner(t *testing.T) { + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMailbox := clientMocks.NewMockMailboxContract(t) + mockMintController := clientMocks.NewMockMintControllerContract(t) + mockWarpISM := clientMocks.NewMockWarpISMContract(t) + mockCosmosClient := cosmosMocks.NewMockCosmosClient(t) + + mnemonic := "infant apart enroll relief kangaroo patch awesome wagon trap feature armor approve" + + config := models.EthereumNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:8545", + TimeoutMS: 1000, + ChainID: 1, + ChainName: "Ethereum", + MailboxAddress: "0x0000000000000000000000000000000000000000", + MintControllerAddress: "0x0000000000000000000000000000000000000000", + OmniTokenAddress: "0x0000000000000000000000000000000000000000", + WarpISMAddress: "0x0000000000000000000000000000000000000000", + OracleAddresses: []string{"0x0E90A32Df6f6143F1A91c25d9552dCbc789C34Eb", "0x958d1F55E14Cba24a077b9634F16f83565fc9411", "0x4c672Edd2ec8eac8f0F1709f33de9A2E786e6912"}, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + cosmosNetwork := models.CosmosNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:36657", + GRPCEnabled: true, + GRPCHost: "localhost", + GRPCPort: 9090, + TimeoutMS: 1000, + ChainID: "poktroll", + ChainName: "Poktroll", + TxFee: 1000, + Bech32Prefix: "pokt", + CoinDenom: "upokt", + MultisigAddress: "pokt13tsl3aglfyzf02n7x28x2ajzw94muu6y57k2ar", + MultisigPublicKeys: []string{"026892de2ec7fdf3125bc1bfd2ff2590d2c9ba756f98a05e9e843ac4d2a1acd4d9", "02faaaf0f385bb17381f36dcd86ab2486e8ff8d93440436496665ac007953076c2", "02cae233806460db75a941a269490ca5165a620b43241edb8bc72e169f4143a6df"}, + MultisigThreshold: 2, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + ethNetworks := []models.EthereumNetworkConfig{ + config, + } + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + ethNewWarpISMContract = func(ethcommon.Address, bind.ContractBackend) (eth.WarpISMContract, error) { + return mockWarpISM, nil + } + + cosmosNewClient = func(models.CosmosNetworkConfig) (cosmos.CosmosClient, error) { + return mockCosmosClient, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + ethNewMintControllerContract = eth.NewMintControllerContract + ethNewWarpISMContract = eth.NewWarpISMContract + cosmosNewClient = cosmos.NewClient + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) + + mockCosmosClient.EXPECT().GetLatestBlockHeight().Return(int64(100), nil) + + mockClient.EXPECT().GetClient().Return(nil) + + mockClient.EXPECT().Chain().Return(models.Chain{ChainDomain: uint32(1)}) + + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(3), nil) + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(2), nil) + mockWarpISM.EXPECT().Eip712Domain(mock.Anything).Return(util.DomainData{ChainId: big.NewInt(1), VerifyingContract: ethcommon.HexToAddress(config.WarpISMAddress)}, nil) + mockMintController.EXPECT().MaxMintLimit(mock.Anything).Return(big.NewInt(100), nil) + + runnable := NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + + assert.NotNil(t, runnable) + + monitor, ok := runnable.(*EthMessageSignerRunnable) + assert.True(t, ok) + assert.Equal(t, mockDB, monitor.db) + assert.Equal(t, mockClient, monitor.client) + assert.Equal(t, mockMintController, monitor.mintController) + assert.Equal(t, mockCosmosClient, monitor.cosmosClient) + assert.Equal(t, uint64(100), monitor.currentEthereumBlockHeight) + assert.Equal(t, uint64(100), monitor.currentCosmosBlockHeight) + +} + +func TestNewMessageSignerFailures(t *testing.T) { + + defer func() { log.StandardLogger().ExitFunc = nil }() + log.StandardLogger().ExitFunc = func(num int) { panic(fmt.Sprintf("exit %d", num)) } + + mockDB := mocks.NewMockDB(t) + mockClient := clientMocks.NewMockEthereumClient(t) + mockMailbox := clientMocks.NewMockMailboxContract(t) + mockMintController := clientMocks.NewMockMintControllerContract(t) + mockWarpISM := clientMocks.NewMockWarpISMContract(t) + mockCosmosClient := cosmosMocks.NewMockCosmosClient(t) + + mnemonic := "infant apart enroll relief kangaroo patch awesome wagon trap feature armor approve" + + config := models.EthereumNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:8545", + TimeoutMS: 1000, + ChainID: 1, + ChainName: "Ethereum", + MailboxAddress: "0x0000000000000000000000000000000000000000", + MintControllerAddress: "0x0000000000000000000000000000000000000000", + OmniTokenAddress: "0x0000000000000000000000000000000000000000", + WarpISMAddress: "0x0000000000000000000000000000000000000000", + OracleAddresses: []string{"0x0E90A32Df6f6143F1A91c25d9552dCbc789C34Eb", "0x958d1F55E14Cba24a077b9634F16f83565fc9411", "0x4c672Edd2ec8eac8f0F1709f33de9A2E786e6912"}, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + cosmosNetwork := models.CosmosNetworkConfig{ + StartBlockHeight: 1, + Confirmations: 1, + RPCURL: "http://localhost:36657", + GRPCEnabled: true, + GRPCHost: "localhost", + GRPCPort: 9090, + TimeoutMS: 1000, + ChainID: "poktroll", + ChainName: "Poktroll", + TxFee: 1000, + Bech32Prefix: "pokt", + CoinDenom: "upokt", + MultisigAddress: "pokt13tsl3aglfyzf02n7x28x2ajzw94muu6y57k2ar", + MultisigPublicKeys: []string{"026892de2ec7fdf3125bc1bfd2ff2590d2c9ba756f98a05e9e843ac4d2a1acd4d9", "02faaaf0f385bb17381f36dcd86ab2486e8ff8d93440436496665ac007953076c2", "02cae233806460db75a941a269490ca5165a620b43241edb8bc72e169f4143a6df"}, + MultisigThreshold: 2, + MessageMonitor: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageSigner: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + MessageRelayer: models.ServiceConfig{ + Enabled: true, + IntervalMS: 1000, + }, + } + + ethNetworks := []models.EthereumNetworkConfig{ + config, + } + + ethNewClient = func(config models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + ethNewWarpISMContract = func(ethcommon.Address, bind.ContractBackend) (eth.WarpISMContract, error) { + return mockWarpISM, nil + } + + cosmosNewClient = func(models.CosmosNetworkConfig) (cosmos.CosmosClient, error) { + return mockCosmosClient, nil + } + + dbNewDB = func() db.DB { + return mockDB + } + + defer func() { + ethNewClient = eth.NewClient + ethNewMailboxContract = eth.NewMailboxContract + ethNewMintControllerContract = eth.NewMintControllerContract + ethNewWarpISMContract = eth.NewWarpISMContract + cosmosNewClient = cosmos.NewClient + dbNewDB = db.NewDB + }() + + mockClient.EXPECT().GetBlockHeight().Return(uint64(100), nil) + + mockCosmosClient.EXPECT().GetLatestBlockHeight().Return(int64(100), nil) + + mockClient.EXPECT().GetClient().Return(nil) + + mockClient.EXPECT().Chain().Return(models.Chain{ChainDomain: uint32(1)}) + + t.Run("Disabled", func(t *testing.T) { + config.MessageSigner.Enabled = false + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + config.MessageSigner.Enabled = true + }) + + t.Run("ClientError", func(t *testing.T) { + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + ethNewClient = func(models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + }) + + t.Run("MintControllerError", func(t *testing.T) { + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + ethNewMintControllerContract = func(ethcommon.Address, bind.ContractBackend) (eth.MintControllerContract, error) { + return mockMintController, nil + } + + }) + + t.Run("WarpISMError", func(t *testing.T) { + + ethNewWarpISMContract = func(ethcommon.Address, bind.ContractBackend) (eth.WarpISMContract, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + ethNewWarpISMContract = func(ethcommon.Address, bind.ContractBackend) (eth.WarpISMContract, error) { + return mockWarpISM, nil + } + + }) + + t.Run("MnemonicError", func(t *testing.T) { + + mnemonic = "invalid" + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + mnemonic = "infant apart enroll relief kangaroo patch awesome wagon trap feature armor approve" + + }) + + t.Run("CosmosClientError", func(t *testing.T) { + + cosmosNewClient = func(models.CosmosNetworkConfig) (cosmos.CosmosClient, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + cosmosNewClient = func(models.CosmosNetworkConfig) (cosmos.CosmosClient, error) { + return mockCosmosClient, nil + } + + }) + + t.Run("SecondEthClientError", func(t *testing.T) { + newEthNetworks := append(ethNetworks, models.EthereumNetworkConfig{ + ChainID: 2, + }) + + assert.Equal(t, 2, len(newEthNetworks)) + assert.Equal(t, 1, len(ethNetworks)) + + ethNewClient = func(config models.EthereumNetworkConfig) (eth.EthereumClient, error) { + if config.ChainID == 1 { + return mockClient, nil + } + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, newEthNetworks) + }) + + ethNewClient = func(config models.EthereumNetworkConfig) (eth.EthereumClient, error) { + return mockClient, nil + } + + }) + + t.Run("MailboxError", func(t *testing.T) { + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return nil, assert.AnError + } + + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + + ethNewMailboxContract = func(ethcommon.Address, bind.ContractBackend) (eth.MailboxContract, error) { + return mockMailbox, nil + } + + }) + + t.Run("InvalidSigners", func(t *testing.T) { + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(100), nil).Once() + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(20), nil).Once() + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + }) + + t.Run("InvalidThreshold", func(t *testing.T) { + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(3), nil).Once() + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(20), nil).Once() + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + }) + + t.Run("DomainDataErrorChainID", func(t *testing.T) { + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(3), nil).Once() + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(2), nil).Once() + mockWarpISM.EXPECT().Eip712Domain(mock.Anything).Return(util.DomainData{ChainId: big.NewInt(2), VerifyingContract: ethcommon.HexToAddress(config.WarpISMAddress)}, nil).Once() + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + }) + + t.Run("DomainDataErrorContract", func(t *testing.T) { + mockWarpISM.EXPECT().ValidatorCount(mock.Anything).Return(big.NewInt(3), nil).Once() + mockWarpISM.EXPECT().SignerThreshold(mock.Anything).Return(big.NewInt(2), nil).Once() + mockWarpISM.EXPECT().Eip712Domain(mock.Anything).Return(util.DomainData{ChainId: big.NewInt(1), VerifyingContract: ethcommon.BytesToAddress([]byte("invalid"))}, nil).Once() + assert.Panics(t, func() { + NewMessageSigner(mnemonic, config, cosmosNetwork, ethNetworks) + }) + }) - assert.Equal(t, signer.maximumAmount, big.NewInt(100)) }