From 6f634c3fc78faff406ad36bc2713a6e00d3eb87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20Martini=C4=87?= Date: Tue, 21 Jun 2022 11:24:29 +0200 Subject: [PATCH] Tests for compass communication (#254) --- testutil/keeper/valset.go | 1 - util/slice/filter.go | 11 ++ .../keeper/consensus/consensus_test.go | 3 +- x/consensus/keeper/consensus/mocks/Queuer.go | 10 +- x/evm/keeper/keeper.go | 31 ++- x/evm/keeper/keeper_integration_test.go | 179 ++++++++++++------ x/evm/keeper/msg_server_submit_new_job.go | 18 +- x/evm/types/turnstone_abi.go | 22 ++- x/valset/keeper/keeper.go | 7 +- x/valset/keeper/setup_test.go | 1 - 10 files changed, 182 insertions(+), 101 deletions(-) create mode 100644 util/slice/filter.go diff --git a/testutil/keeper/valset.go b/testutil/keeper/valset.go index 5f356316..8621953a 100644 --- a/testutil/keeper/valset.go +++ b/testutil/keeper/valset.go @@ -42,7 +42,6 @@ func ValsetKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { memStoreKey, paramsSubspace, nil, - nil, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) diff --git a/util/slice/filter.go b/util/slice/filter.go new file mode 100644 index 00000000..a918336d --- /dev/null +++ b/util/slice/filter.go @@ -0,0 +1,11 @@ +package slice + +func Filter[V any](slice []V, filterIn func(val V) bool) []V { + var filtered []V + for _, item := range slice { + if filterIn(item) { + filtered = append(filtered, item) + } + } + return filtered +} diff --git a/x/consensus/keeper/consensus/consensus_test.go b/x/consensus/keeper/consensus/consensus_test.go index 83a42ff8..210b2cad 100644 --- a/x/consensus/keeper/consensus/consensus_test.go +++ b/x/consensus/keeper/consensus/consensus_test.go @@ -82,9 +82,10 @@ func TestConsensusQueueAllMethods(t *testing.T) { sig := &types.SignData{ ValAddress: sdk.ValAddress("bob"), Signature: []byte(`custom signature`), + PublicKey: []byte("it does not matter"), } - cq.AddSignature(ctx, msgs[0].GetId(), []byte("does not matter"), sig) + cq.AddSignature(ctx, msgs[0].GetId(), sig) t.Run("getting all messages should still return one", func(t *testing.T) { var err error diff --git a/x/consensus/keeper/consensus/mocks/Queuer.go b/x/consensus/keeper/consensus/mocks/Queuer.go index 0885917e..1e1c0808 100644 --- a/x/consensus/keeper/consensus/mocks/Queuer.go +++ b/x/consensus/keeper/consensus/mocks/Queuer.go @@ -16,13 +16,13 @@ type Queuer struct { mock.Mock } -// AddSignature provides a mock function with given fields: ctx, id, publickey, signData -func (_m *Queuer) AddSignature(ctx types.Context, id uint64, publickey []byte, signData *consensustypes.SignData) error { - ret := _m.Called(ctx, id, publickey, signData) +// AddSignature provides a mock function with given fields: ctx, id, signData +func (_m *Queuer) AddSignature(ctx types.Context, id uint64, signData *consensustypes.SignData) error { + ret := _m.Called(ctx, id, signData) var r0 error - if rf, ok := ret.Get(0).(func(types.Context, uint64, []byte, *consensustypes.SignData) error); ok { - r0 = rf(ctx, id, publickey, signData) + if rf, ok := ret.Get(0).(func(types.Context, uint64, *consensustypes.SignData) error); ok { + r0 = rf(ctx, id, signData) } else { r0 = ret.Error(0) } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index d593710e..4fbcaa48 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -17,6 +17,9 @@ import ( valsettypes "github.com/palomachain/paloma/x/valset/types" ) +const ( + maxPower = 1 << 32 +) const ( ConsensusArbitraryContractCall = consensustypes.ConsensusQueueType("evm-arbitrary-smart-contract-call") ConsensusTurnstoneMessage = consensustypes.ConsensusQueueType("evm-turnstone-message") @@ -28,9 +31,13 @@ type evmChainTemp struct { turnstoneID string } +func (e evmChainTemp) ChainID() string { + return e.chainID +} + var zero32Byte [32]byte -var supportedChainIDs = []evmChainTemp{ +var SupportedChainIDs = []evmChainTemp{ {"eth-main", string(zero32Byte[:])}, {"ropsten", string(zero32Byte[:])}, } @@ -72,16 +79,24 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) AddSmartContractExecutionToConsensus( ctx sdk.Context, - msg *types.Message, + chainID, + turnstoneID string, + logicCall *types.SubmitLogicCall, ) error { return k.ConsensusKeeper.PutMessageForSigning( ctx, consensustypes.Queue( ConsensusTurnstoneMessage, consensustypes.ChainTypeEVM, - msg.ChainID, + chainID, ), - msg, + &types.Message{ + ChainID: chainID, + TurnstoneID: turnstoneID, + Action: &types.Message_SubmitLogicCall{ + SubmitLogicCall: logicCall, + }, + }, ) } @@ -122,7 +137,7 @@ func (k Keeper) RegisterConsensusQueues(adder consensus.RegistryAdder) { return receivedAddr.Hex() == recoveredAddr.Hex() } - for _, chain := range supportedChainIDs { + for _, chain := range SupportedChainIDs { adder.AddConcencusQueueType( false, consensus.WithChainInfo(consensustypes.ChainTypeEVM, chain.chainID), @@ -152,12 +167,8 @@ func (k Keeper) RegisterConsensusQueues(adder consensus.RegistryAdder) { } -const ( - maxPower = 1 << 32 -) - func (k Keeper) OnSnapshotBuilt(ctx sdk.Context, snapshot *valsettypes.Snapshot) { - for _, chain := range supportedChainIDs { + for _, chain := range SupportedChainIDs { valset := transformSnapshotToTurnstoneValset(snapshot, chain.chainID) k.ConsensusKeeper.PutMessageForSigning( diff --git a/x/evm/keeper/keeper_integration_test.go b/x/evm/keeper/keeper_integration_test.go index 4cf57e23..b17b938a 100644 --- a/x/evm/keeper/keeper_integration_test.go +++ b/x/evm/keeper/keeper_integration_test.go @@ -1,14 +1,29 @@ package keeper_test import ( + "math/big" + "strings" "testing" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/palomachain/paloma/app" "github.com/palomachain/paloma/testutil/rand" + "github.com/palomachain/paloma/testutil/sample" + "github.com/palomachain/paloma/util/slice" + consensustypes "github.com/palomachain/paloma/x/consensus/types" + "github.com/palomachain/paloma/x/evm/keeper" + "github.com/palomachain/paloma/x/evm/types" + valsettypes "github.com/palomachain/paloma/x/valset/types" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/ed25519" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/vizualni/whoops" ) func genValidators(t *testing.T, numValidators, totalConsPower int) []stakingtypes.Validator { @@ -43,66 +58,104 @@ func genValidators(t *testing.T, numValidators, totalConsPower int) []stakingtyp return validators } -// func TestEndToEndForEvmArbitraryCall(t *testing.T) { -// chainType, chainID := consensustypes.ChainTypeEVM, "eth-main" -// a := app.NewTestApp(t, false) -// ctx := a.NewContext(false, tmproto.Header{ -// Height: 5, -// }) - -// validators := genValidators(t, 25, 25000) -// for _, val := range validators { -// a.StakingKeeper.SetValidator(ctx, val) -// } - -// smartContractAddr := common.BytesToAddress(rand.Bytes(5)) -// err := a.EvmKeeper.AddSmartContractExecutionToConsensus( -// ctx, -// &types.ArbitrarySmartContractCall{ -// Payload: func() []byte { -// evm := whoops.Must(abi.JSON(strings.NewReader(sample.SimpleABI))) -// return whoops.Must(evm.Pack("store", big.NewInt(1337))) -// }(), -// HexAddress: smartContractAddr.Hex(), -// Abi: []byte(sample.SimpleABI), -// }, -// ) - -// require.NoError(t, err) - -// private, err := crypto.GenerateKey() -// require.NoError(t, err) - -// accAddr := crypto.PubkeyToAddress(private.PublicKey) -// err = a.ValsetKeeper.AddExternalChainInfo(ctx, validators[0].GetOperator(), []*valsettypes.ExternalChainInfo{ -// { -// ChainType: chainType, -// ChainID: chainID, -// Address: accAddr.Hex(), -// Pubkey: accAddr[:], -// }, -// }) - -// require.NoError(t, err) -// queue := consensustypes.Queue(keeper.ConsensusArbitraryContractCall, chainType, chainID) -// msgs, err := a.ConsensusKeeper.GetMessagesForSigning(ctx, queue, validators[0].GetOperator()) - -// for _, msg := range msgs { -// sigbz, err := crypto.Sign(msg.GetBytesToSign(), private) -// require.NoError(t, err) -// err = a.ConsensusKeeper.AddMessageSignature( -// ctx, -// validators[0].GetOperator(), -// []*consensustypes.MsgAddMessagesSignatures_MsgSignedMessage{ -// { -// Id: msg.GetId(), -// QueueTypeName: queue, -// Signature: sigbz, -// SignedByAddress: accAddr.Hex(), -// }, -// }, -// ) -// require.NoError(t, err) -// } - -// } +func TestEndToEndForEvmArbitraryCall(t *testing.T) { + chainType, chainID := consensustypes.ChainTypeEVM, "eth-main" + a := app.NewTestApp(t, false) + ctx := a.NewContext(false, tmproto.Header{ + Height: 5, + }) + + validators := genValidators(t, 25, 25000) + for _, val := range validators { + a.StakingKeeper.SetValidator(ctx, val) + } + + smartContractAddr := common.BytesToAddress(rand.Bytes(5)) + err := a.EvmKeeper.AddSmartContractExecutionToConsensus( + ctx, + chainID, + "", + &types.SubmitLogicCall{ + Payload: func() []byte { + evm := whoops.Must(abi.JSON(strings.NewReader(sample.SimpleABI))) + return whoops.Must(evm.Pack("store", big.NewInt(1337))) + }(), + HexContractAddress: smartContractAddr.Hex(), + Abi: []byte(sample.SimpleABI), + Deadline: 1337, + }, + ) + + require.NoError(t, err) + + private, err := crypto.GenerateKey() + require.NoError(t, err) + + accAddr := crypto.PubkeyToAddress(private.PublicKey) + err = a.ValsetKeeper.AddExternalChainInfo(ctx, validators[0].GetOperator(), []*valsettypes.ExternalChainInfo{ + { + ChainType: chainType, + ChainID: chainID, + Address: accAddr.Hex(), + Pubkey: accAddr[:], + }, + }) + + require.NoError(t, err) + queue := consensustypes.Queue(keeper.ConsensusArbitraryContractCall, chainType, chainID) + msgs, err := a.ConsensusKeeper.GetMessagesForSigning(ctx, queue, validators[0].GetOperator()) + + for _, msg := range msgs { + sigbz, err := crypto.Sign(msg.GetBytesToSign(), private) + require.NoError(t, err) + err = a.ConsensusKeeper.AddMessageSignature( + ctx, + validators[0].GetOperator(), + []*consensustypes.MsgAddMessagesSignatures_MsgSignedMessage{ + { + Id: msg.GetId(), + QueueTypeName: queue, + Signature: sigbz, + SignedByAddress: accAddr.Hex(), + }, + }, + ) + require.NoError(t, err) + } + +} + +func TestOnSnapshotBuilt(t *testing.T) { + a := app.NewTestApp(t, false) + ctx := a.NewContext(false, tmproto.Header{ + Height: 5, + }) + + validators := genValidators(t, 25, 25000) + for _, val := range validators { + a.StakingKeeper.SetValidator(ctx, val) + } + + var supportedChainIDs []string + for _, c := range keeper.SupportedChainIDs { + supportedChainIDs = append(supportedChainIDs, c.ChainID()) + } + + require.Empty(t, slice.Filter(supportedChainIDs, func(chainID string) bool { + queue := consensustypes.Queue(keeper.ConsensusTurnstoneMessage, consensustypes.ChainTypeEVM, chainID) + msgs, err := a.ConsensusKeeper.GetMessagesFromQueue(ctx, queue, 1) + require.NoError(t, err) + return len(msgs) > 0 + })) + + snapshot, err := a.ValsetKeeper.TriggerSnapshotBuild(ctx) + require.NoError(t, err) + a.EvmKeeper.OnSnapshotBuilt(ctx, snapshot) + + require.Len(t, slice.Filter(supportedChainIDs, func(chainID string) bool { + queue := consensustypes.Queue(keeper.ConsensusTurnstoneMessage, consensustypes.ChainTypeEVM, chainID) + msgs, err := a.ConsensusKeeper.GetMessagesFromQueue(ctx, queue, 1) + require.NoError(t, err) + return len(msgs) > 0 + }), 2) +} diff --git a/x/evm/keeper/msg_server_submit_new_job.go b/x/evm/keeper/msg_server_submit_new_job.go index aac59dfb..d49dd1af 100644 --- a/x/evm/keeper/msg_server_submit_new_job.go +++ b/x/evm/keeper/msg_server_submit_new_job.go @@ -21,17 +21,13 @@ func (k msgServer) SubmitNewJob(goCtx context.Context, msg *types.MsgSubmitNewJo err := k.AddSmartContractExecutionToConsensus( ctx, - &types.Message{ - ChainID: msg.ChainID, - TurnstoneID: string(zero32Byte[:]), - Action: &types.Message_SubmitLogicCall{ - SubmitLogicCall: &types.SubmitLogicCall{ - HexContractAddress: msg.GetHexSmartContractAddress(), - Payload: common.Hex2Bytes(msg.GetHexPayload()), - Abi: []byte(msg.GetAbi()), - Deadline: ctx.BlockTime().UTC().Add(5 * time.Minute).Unix(), - }, - }, + msg.GetChainID(), + string(zero32Byte[:]), + &types.SubmitLogicCall{ + HexContractAddress: msg.GetHexSmartContractAddress(), + Payload: common.Hex2Bytes(msg.GetHexPayload()), + Abi: []byte(msg.GetAbi()), + Deadline: ctx.BlockTime().UTC().Add(5 * time.Minute).Unix(), }, ) diff --git a/x/evm/types/turnstone_abi.go b/x/evm/types/turnstone_abi.go index 3c127475..54471fa8 100644 --- a/x/evm/types/turnstone_abi.go +++ b/x/evm/types/turnstone_abi.go @@ -66,31 +66,39 @@ func (_m *Message_SubmitLogicCall) keccak256(orig *Message, nonce uint64) []byte arguments := abi.Arguments{ // arguments {Type: whoops.Must(abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - {Type: "address"}, - {Type: "bytes"}, + {Name: "address", Type: "address"}, + {Name: "payload", Type: "bytes"}, }))}, // message id {Type: whoops.Must(abi.NewType("uint256", "", nil))}, // turnstone id - {Type: whoops.Must(abi.NewType("bytes", "", nil))}, + {Type: whoops.Must(abi.NewType("bytes32", "", nil))}, // deadline {Type: whoops.Must(abi.NewType("uint256", "", nil))}, } + method := abi.NewMethod("logic_call", "logic_call", abi.Function, "", false, false, arguments, abi.Arguments{}) + var bytes32 [32]byte + + copy(bytes32[:], orig.GetTurnstoneID()) bytes, err := arguments.Pack( - []any{ + struct { + Address common.Address + Payload []byte + }{ common.HexToAddress(m.GetHexContractAddress()), m.GetPayload(), }, - new(big.Int).SetInt64(int64(nonce)).Bytes(), - orig.GetTurnstoneID(), - m.GetDeadline(), + new(big.Int).SetInt64(int64(nonce)), + bytes32, + big.NewInt(m.GetDeadline()), ) if err != nil { panic(err) } + bytes = append(method.ID[:], bytes...) return crypto.Keccak256(bytes) diff --git a/x/valset/keeper/keeper.go b/x/valset/keeper/keeper.go index 0e0d66f5..f6c94830 100644 --- a/x/valset/keeper/keeper.go +++ b/x/valset/keeper/keeper.go @@ -141,14 +141,17 @@ func (k Keeper) RemoveExternalChainInfo(ctx sdk.Context) {} // TriggerSnapshotBuild creates the snapshot of currently active validators that are // active and registered as conductors. -func (k Keeper) TriggerSnapshotBuild(ctx sdk.Context) { +func (k Keeper) TriggerSnapshotBuild(ctx sdk.Context) (*types.Snapshot, error) { snapshot, err := k.createSnapshot(ctx) if err != nil { - panic(err) + return nil, err } + for _, listener := range k.SnapshotListeners { listener.OnSnapshotBuilt(ctx, snapshot) } + + return snapshot, err } // createSnapshot builds a current snapshot of validators. diff --git a/x/valset/keeper/setup_test.go b/x/valset/keeper/setup_test.go index 84b6810d..82fdd829 100644 --- a/x/valset/keeper/setup_test.go +++ b/x/valset/keeper/setup_test.go @@ -52,7 +52,6 @@ func newValsetKeeper(t testing.TB) (*Keeper, mockedServices, sdk.Context) { memStoreKey, paramsSubspace, ms.StakingKeeper, - nil, ) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil)