diff --git a/challenger/types/config.go b/challenger/types/config.go index 24fec14..220a3a2 100644 --- a/challenger/types/config.go +++ b/challenger/types/config.go @@ -113,6 +113,7 @@ func (cfg Config) Validate() error { func (cfg Config) L1NodeConfig() nodetypes.NodeConfig { nc := nodetypes.NodeConfig{ + ChainID: cfg.L1Node.ChainID, RPC: cfg.L1Node.RPCAddress, ProcessType: nodetypes.PROCESS_TYPE_DEFAULT, Bech32Prefix: cfg.L1Node.Bech32Prefix, @@ -122,6 +123,7 @@ func (cfg Config) L1NodeConfig() nodetypes.NodeConfig { func (cfg Config) L2NodeConfig() nodetypes.NodeConfig { nc := nodetypes.NodeConfig{ + ChainID: cfg.L2Node.ChainID, RPC: cfg.L2Node.RPCAddress, ProcessType: nodetypes.PROCESS_TYPE_DEFAULT, Bech32Prefix: cfg.L2Node.Bech32Prefix, diff --git a/e2e/batch_reconstruction_test.go b/e2e/batch_reconstruction_test.go index b005696..3f355d8 100644 --- a/e2e/batch_reconstruction_test.go +++ b/e2e/batch_reconstruction_test.go @@ -16,6 +16,8 @@ import ( cmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-proto/anyutil" gogoproto "github.com/cosmos/gogoproto/proto" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibctmlightclients "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" opchildv1 "github.com/initia-labs/OPinit/api/opinit/opchild/v1" ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" executortypes "github.com/initia-labs/opinit-bots/executor/types" @@ -35,7 +37,7 @@ func TestBatchReconstructionTest(t *testing.T) { Gas: "auto", GasPrices: "0.025uinit", GasAdjustment: 1.2, - TrustingPeriod: "168h", + TrustingPeriod: "1h", NumValidators: 1, NumFullNodes: 0, } @@ -49,7 +51,7 @@ func TestBatchReconstructionTest(t *testing.T) { Gas: "auto", GasPrices: "0.025umin", GasAdjustment: 1.2, - TrustingPeriod: "168h", + TrustingPeriod: "1h", NumValidators: 1, NumFullNodes: 0, } @@ -65,7 +67,24 @@ func TestBatchReconstructionTest(t *testing.T) { cases := []struct { name string daChainConfig DAChainConfig + relayerImpl ibc.RelayerImplementation }{ + { + name: "initia with go relayer", + daChainConfig: DAChainConfig{ + ChainConfig: *l1ChainConfig, + ChainType: ophosttypes.BatchInfo_CHAIN_TYPE_INITIA, + }, + relayerImpl: ibc.CosmosRly, + }, + { + name: "initia with hermes relayer", + daChainConfig: DAChainConfig{ + ChainConfig: *l1ChainConfig, + ChainType: ophosttypes.BatchInfo_CHAIN_TYPE_INITIA, + }, + relayerImpl: ibc.Hermes, + }, { name: "celestia", daChainConfig: DAChainConfig{ @@ -78,19 +97,13 @@ func TestBatchReconstructionTest(t *testing.T) { Gas: "auto", GasPrices: "0.25utia", GasAdjustment: 1.5, - TrustingPeriod: "168h", + TrustingPeriod: "1h", NumValidators: 1, NumFullNodes: 0, }, ChainType: ophosttypes.BatchInfo_CHAIN_TYPE_CELESTIA, }, - }, - { - name: "initia", - daChainConfig: DAChainConfig{ - ChainConfig: *l1ChainConfig, - ChainType: ophosttypes.BatchInfo_CHAIN_TYPE_INITIA, - }, + relayerImpl: ibc.CosmosRly, }, } @@ -98,7 +111,7 @@ func TestBatchReconstructionTest(t *testing.T) { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() - op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, &tc.daChainConfig, bridgeConfig) + op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, &tc.daChainConfig, bridgeConfig, tc.relayerImpl) err := testutil.WaitForBlocks(ctx, 20, op.Initia, op.Minitia) require.NoError(t, err) @@ -142,7 +155,7 @@ func TestBatchReconstructionTest(t *testing.T) { require.NoError(t, err) require.NotNil(t, block) - err = fillOracleData(ctx, block, op.Initia) + err = fillData(ctx, block, op.Initia) require.NoError(t, err) pbb, err := block.ToProto() @@ -243,7 +256,7 @@ func BlockFromProtoWithNoValidation(bp *cmtproto.Block) (*cmtypes.Block, error) return b, nil } -func fillOracleData(ctx context.Context, block *cmtypes.Block, chain *L1Chain) error { +func fillData(ctx context.Context, block *cmtypes.Block, chain *L1Chain) error { for i, txBytes := range block.Txs { var raw txv1beta1.TxRaw if err := proto.Unmarshal(txBytes, &raw); err != nil { @@ -305,6 +318,91 @@ func fillOracleData(ctx context.Context, block *cmtypes.Block, chain *L1Chain) e if err != nil { return errors.Join(errors.New("failed to marshal oracle msg"), err) } + case "/ibc.core.client.v1.MsgUpdateClient": + updateClientMsg := new(ibcclienttypes.MsgUpdateClient) + err := updateClientMsg.Unmarshal(anyMsg.Value) + if err != nil { + return err + } + + if updateClientMsg.ClientMessage.TypeUrl != "/ibc.lightclients.tendermint.v1.Header" { + continue + } + + tmHeader := new(ibctmlightclients.Header) + err = tmHeader.Unmarshal(updateClientMsg.ClientMessage.Value) + if err != nil { + return err + } + + // fill ValidatorSet + height := tmHeader.SignedHeader.Commit.Height + validators, err := getAllValidators(ctx, chain, height) + if err != nil { + return err + } + cmtValidators, _, err := toCmtProtoValidators(validators) + if err != nil { + return err + } + if tmHeader.ValidatorSet == nil { + tmHeader.ValidatorSet = new(cmtproto.ValidatorSet) + } + tmHeader.ValidatorSet.Validators = cmtValidators + for _, val := range cmtValidators { + if bytes.Equal(val.Address, tmHeader.SignedHeader.Header.ProposerAddress) { + tmHeader.ValidatorSet.Proposer = val + } + } + + // fill TrustedValidators + height = int64(tmHeader.TrustedHeight.RevisionHeight) + validators, err = getAllValidators(ctx, chain, height) + if err != nil { + return err + } + cmtValidators, _, err = toCmtProtoValidators(validators) + if err != nil { + return err + } + blockHeader, err := chain.GetFullNode().Client.Header(ctx, &height) + if err != nil { + return err + } + if tmHeader.TrustedValidators == nil { + tmHeader.TrustedValidators = new(cmtproto.ValidatorSet) + } + tmHeader.TrustedValidators.Validators = cmtValidators + for _, val := range cmtValidators { + if bytes.Equal(val.Address, blockHeader.Header.ProposerAddress.Bytes()) { + tmHeader.TrustedValidators.Proposer = val + } + } + + // fill commit signatures + height = tmHeader.SignedHeader.Commit.Height + 1 + block, err := chain.GetFullNode().Client.Block(ctx, &height) + if err != nil { + return err + } + + for sigIndex, signature := range tmHeader.SignedHeader.Commit.Signatures { + if len(signature.Signature) == 2 { + // fill signature + blockSigIndex := int(signature.Signature[0]) + int(signature.Signature[1])<<8 + tmHeader.SignedHeader.Commit.Signatures[sigIndex] = *block.Block.LastCommit.Signatures[blockSigIndex].ToProto() + } + } + + updateClientMsg.ClientMessage.Value, err = tmHeader.Marshal() + if err != nil { + return errors.Join(errors.New("failed to marshal tm header"), err) + } + + anyMsg.Value, err = updateClientMsg.Marshal() + if err != nil { + return errors.Join(errors.New("failed to marshal update client msg"), err) + } default: continue } @@ -323,3 +421,39 @@ func fillOracleData(ctx context.Context, block *cmtypes.Block, chain *L1Chain) e } return nil } + +func getAllValidators(ctx context.Context, chain *L1Chain, height int64) ([]*cmtypes.Validator, error) { + page := 1 + perPage := 100 + + validators := make([]*cmtypes.Validator, 0) + for { + result, err := chain.GetFullNode().Client.Validators(ctx, &height, &page, &perPage) + if err != nil { + return nil, err + } + validators = append(validators, result.Validators...) + page++ + if len(validators) >= result.Total { + break + } + } + return validators, nil +} + +func toCmtProtoValidators(validators []*cmtypes.Validator) ([]*cmtproto.Validator, int64, error) { + protoValidators := make([]*cmtproto.Validator, 0, len(validators)) + totalVotingPower := int64(0) + + for i := range validators { + protoValidator, err := validators[i].ToProto() + if err != nil { + return nil, 0, err + } + protoValidator.ProposerPriority = 0 + totalVotingPower += protoValidator.VotingPower + + protoValidators = append(protoValidators, protoValidator) + } + return protoValidators, totalVotingPower, nil +} diff --git a/e2e/docker.go b/e2e/docker.go index 8090b3c..38ec194 100644 --- a/e2e/docker.go +++ b/e2e/docker.go @@ -494,7 +494,7 @@ func (op *DockerOPBot) pullContainerImageIfNecessary(containerImage ibc.DockerIm } func (op *DockerOPBot) Name() string { - return op.c.Name() + "-" + op.botName + "-" + dockerutil.SanitizeContainerName(op.testName) + return dockerutil.CondenseHostName(dockerutil.SanitizeContainerName(op.c.Name() + "-" + op.botName + "-" + op.testName)) } func (op *DockerOPBot) Bind() []string { diff --git a/e2e/go.mod b/e2e/go.mod index a121a8a..5e25037 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -101,7 +101,7 @@ require ( github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/iavl v1.2.0 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect - github.com/cosmos/ibc-go/v8 v8.5.0 // indirect + github.com/cosmos/ibc-go/v8 v8.5.0 github.com/cosmos/ics23/go v0.11.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect diff --git a/e2e/helper.go b/e2e/helper.go index 7541472..b30fa45 100644 --- a/e2e/helper.go +++ b/e2e/helper.go @@ -13,6 +13,8 @@ import ( "cosmossdk.io/math" "cosmossdk.io/x/feegrant" "github.com/avast/retry-go/v4" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibctmlightclients "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" "github.com/icza/dyno" oracleconfig "github.com/skip-mev/connect/v2/oracle/config" "github.com/skip-mev/connect/v2/providers/apis/marketmap" @@ -118,6 +120,8 @@ func MinitiaEncoding() *cosmostestutil.TestEncodingConfig { cfg := cosmos.DefaultEncoding() authz.RegisterInterfaces(cfg.InterfaceRegistry) feegrant.RegisterInterfaces(cfg.InterfaceRegistry) + ibcclienttypes.RegisterInterfaces(cfg.InterfaceRegistry) + ibctmlightclients.RegisterInterfaces(cfg.InterfaceRegistry) opchildtypes.RegisterInterfaces(cfg.InterfaceRegistry) return &cfg } @@ -130,6 +134,7 @@ func SetupTest( l2ChainConfig *ChainConfig, daChainConfig *DAChainConfig, bridgeConfig *BridgeConfig, + relayerImpl ibc.RelayerImplementation, ) OPTestHelper { require.NotNil(t, l1ChainConfig) require.NotNil(t, l2ChainConfig) @@ -383,7 +388,7 @@ func SetupTest( // relayer setup - relayer := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build(t, client, network) + relayer := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build(t, client, network) ic := interchaintest.NewInterchain(). AddChain(initia). @@ -394,6 +399,9 @@ func SetupTest( Chain2: minitia, Relayer: relayer, Path: ibcPath, + CreateClientOpts: ibc.CreateClientOptions{ + TrustingPeriod: initia.Config().TrustingPeriod, + }, }) da := initia @@ -496,7 +504,6 @@ func SetupTest( }) err = op.WaitForSync(ctx) require.NoError(t, err) - return helper } @@ -558,7 +565,7 @@ func (op OPTestHelper) ExecutorConfig() *executortypes.Config { L1Node: executortypes.NodeConfig{ ChainID: op.Initia.Config().ChainID, Bech32Prefix: op.Initia.Config().Bech32Prefix, - RPCAddress: fmt.Sprintf("http://%s:26657", op.Initia.GetFullNode().Name()), + RPCAddress: fmt.Sprintf("http://%s:26657", op.Initia.GetFullNode().HostName()), GasPrice: op.Initia.Config().GasPrices, GasAdjustment: op.Initia.Config().GasAdjustment, TxTimeout: 10, @@ -567,7 +574,7 @@ func (op OPTestHelper) ExecutorConfig() *executortypes.Config { L2Node: executortypes.NodeConfig{ ChainID: op.Minitia.Config().ChainID, Bech32Prefix: op.Minitia.Config().Bech32Prefix, - RPCAddress: fmt.Sprintf("http://%s:26657", op.Minitia.GetFullNode().Name()), + RPCAddress: fmt.Sprintf("http://%s:26657", op.Minitia.GetFullNode().HostName()), GasPrice: "", GasAdjustment: op.Minitia.Config().GasAdjustment, TxTimeout: 10, @@ -576,7 +583,7 @@ func (op OPTestHelper) ExecutorConfig() *executortypes.Config { DANode: executortypes.NodeConfig{ ChainID: op.DA.Config().ChainID, Bech32Prefix: op.DA.Config().Bech32Prefix, - RPCAddress: fmt.Sprintf("http://%s:26657", op.DA.GetFullNode().Name()), + RPCAddress: fmt.Sprintf("http://%s:26657", op.DA.GetFullNode().HostName()), GasPrice: op.DA.Config().GasPrices, GasAdjustment: op.DA.Config().GasAdjustment, TxTimeout: 10, diff --git a/e2e/multiple_txs_test.go b/e2e/multiple_txs_test.go index 4939dca..6311204 100644 --- a/e2e/multiple_txs_test.go +++ b/e2e/multiple_txs_test.go @@ -59,7 +59,7 @@ func TestMultipleDepositsAndWithdrawals(t *testing.T) { ctx := context.Background() - op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, daChainConfig, bridgeConfig) + op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, daChainConfig, bridgeConfig, ibc.CosmosRly) user0 := interchaintest.GetAndFundTestUsers(t, ctx, "user", math.NewInt(100_000), op.Initia, op.Minitia) user1 := interchaintest.GetAndFundTestUsers(t, ctx, "user", math.NewInt(100_000), op.Initia, op.Minitia) diff --git a/e2e/reconnect_node_test.go b/e2e/reconnect_node_test.go index b1e4b03..c23e2ad 100644 --- a/e2e/reconnect_node_test.go +++ b/e2e/reconnect_node_test.go @@ -54,7 +54,7 @@ func TestReconnectNodes(t *testing.T) { ctx := context.Background() - op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, daChainConfig, bridgeConfig) + op := SetupTest(t, ctx, BotExecutor, l1ChainConfig, l2ChainConfig, daChainConfig, bridgeConfig, ibc.CosmosRly) err := op.Relayer.PauseRelayer(ctx) require.NoError(t, err) diff --git a/executor/batchsubmitter/batch_submitter.go b/executor/batchsubmitter/batch_submitter.go index b9c6c0f..88e33b8 100644 --- a/executor/batchsubmitter/batch_submitter.go +++ b/executor/batchsubmitter/batch_submitter.go @@ -10,6 +10,7 @@ import ( ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" + coretypes "github.com/cometbft/cometbft/rpc/core/types" executortypes "github.com/initia-labs/opinit-bots/executor/types" "github.com/initia-labs/opinit-bots/node" btypes "github.com/initia-labs/opinit-bots/node/broadcaster/types" @@ -20,7 +21,9 @@ import ( ) type hostNode interface { + ChainId() string QueryBatchInfos(context.Context, uint64) (*ophosttypes.QueryBatchInfosResponse, error) + QueryBlock(ctx context.Context, height int64) (*coretypes.ResultBlock, error) } type BatchSubmitter struct { diff --git a/executor/batchsubmitter/common_test.go b/executor/batchsubmitter/common_test.go index b0d84b2..4eb4faa 100644 --- a/executor/batchsubmitter/common_test.go +++ b/executor/batchsubmitter/common_test.go @@ -3,11 +3,14 @@ package batchsubmitter import ( "context" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + cmttypes "github.com/cometbft/cometbft/types" ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" executortypes "github.com/initia-labs/opinit-bots/executor/types" btypes "github.com/initia-labs/opinit-bots/node/broadcaster/types" nodetypes "github.com/initia-labs/opinit-bots/node/types" "github.com/initia-labs/opinit-bots/types" + "github.com/pkg/errors" "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" @@ -17,11 +20,15 @@ import ( type mockHost struct { batchInfos []ophosttypes.BatchInfoWithOutput + blocks map[int64]*cmttypes.Block + chainId string } -func NewMockHost(batchInfos []ophosttypes.BatchInfoWithOutput) *mockHost { +func NewMockHost(batchInfos []ophosttypes.BatchInfoWithOutput, chainId string) *mockHost { return &mockHost{ batchInfos: batchInfos, + blocks: make(map[int64]*cmttypes.Block), + chainId: chainId, } } @@ -31,6 +38,23 @@ func (m *mockHost) QueryBatchInfos(ctx context.Context, bridgeId uint64) (*ophos }, nil } +func (m *mockHost) QueryBlock(ctx context.Context, height int64) (*coretypes.ResultBlock, error) { + if block, ok := m.blocks[height]; ok { + return &coretypes.ResultBlock{ + Block: block, + }, nil + } + return nil, errors.New("block not found") +} + +func (m *mockHost) SetBlock(height int64, block *cmttypes.Block) { + m.blocks[height] = block +} + +func (m *mockHost) ChainId() string { + return m.chainId +} + var _ hostNode = (*mockHost)(nil) type mockDA struct { diff --git a/executor/batchsubmitter/data_conversion.go b/executor/batchsubmitter/data_conversion.go new file mode 100644 index 0000000..10f1f5c --- /dev/null +++ b/executor/batchsubmitter/data_conversion.go @@ -0,0 +1,183 @@ +package batchsubmitter + +import ( + "bytes" + "slices" + "time" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + + cmttypes "github.com/cometbft/cometbft/types" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibctmlightclients "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" + childprovider "github.com/initia-labs/opinit-bots/provider/child" + "github.com/initia-labs/opinit-bots/txutils" + "github.com/initia-labs/opinit-bots/types" + "github.com/pkg/errors" +) + +// emptyOracleData converts the MsgUpdateOracle messages's data field to empty +// to decrease the size of the batch. +func (bs *BatchSubmitter) emptyOracleData(pbb *cmtproto.Block) (*cmtproto.Block, error) { + txs := pbb.Data.GetTxs() + if len(txs) == 0 { + return pbb, nil + } + txBytes := txs[0] + + txConfig := bs.node.GetTxConfig() + tx, err := txutils.DecodeTx(txConfig, txBytes) + if err != nil { + // ignore not registered tx in codec + return pbb, nil + } + + msgs := tx.GetMsgs() + // oracle tx has only one message + if len(msgs) != 1 { + return pbb, nil + } + + switch msg := msgs[0].(type) { + case *opchildtypes.MsgUpdateOracle: + msg.Data = []byte{} + case *authz.MsgExec: + if len(msg.Msgs) != 1 || msg.Msgs[0].TypeUrl != "/opinit.opchild.v1.MsgUpdateOracle" { + return pbb, nil + } + oracleMsg := &opchildtypes.MsgUpdateOracle{} + err = bs.node.Codec().UnpackAny(msg.Msgs[0], &oracleMsg) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack oracle msg from authz msg") + } + oracleMsg.Data = []byte{} + msgs[0], err = childprovider.CreateAuthzMsg(msg.Grantee, oracleMsg) + if err != nil { + return nil, errors.Wrap(err, "failed to create authz msg") + } + } + + tx, err = txutils.ChangeMsgsFromTx(txConfig, tx, []sdk.Msg{msgs[0]}) + if err != nil { + return nil, errors.Wrap(err, "failed to change msgs from tx") + } + convertedTxBytes, err := txutils.EncodeTx(txConfig, tx) + if err != nil { + return nil, errors.Wrap(err, "failed to encode tx") + } + pbb.Data.Txs[0] = convertedTxBytes + + return pbb, nil +} + +// emptyUpdateClientData converts the MsgUpdateClient messages's validator set and part of signature fields to empty +func (bs *BatchSubmitter) emptyUpdateClientData(ctx types.Context, pbb *cmtproto.Block) (*cmtproto.Block, error) { + blockQuerier := func(height int64) (*coretypes.ResultBlock, error) { + ticker := time.NewTicker(ctx.PollingInterval()) + defer ticker.Stop() + + var block *coretypes.ResultBlock + var err error + for retry := 1; retry <= types.MaxRetryCount; retry++ { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-ticker.C: + } + block, err = bs.host.QueryBlock(ctx, height) + if err != nil { + continue + } + } + return block, nil + } + + for txIndex, txBytes := range pbb.Data.GetTxs() { + txConfig := bs.node.GetTxConfig() + tx, err := txutils.DecodeTx(txConfig, txBytes) + if err != nil { + // ignore not registered tx in codec + continue + } + + msgs := tx.GetMsgs() + for msgIndex, msg := range msgs { + switch msg := msg.(type) { + case *ibcclienttypes.MsgUpdateClient: + if msg.ClientMessage.TypeUrl != "/ibc.lightclients.tendermint.v1.Header" { + continue + } + + clientMsg := &ibctmlightclients.Header{} + err = bs.node.Codec().UnpackAny(msg.ClientMessage, &clientMsg) + if err != nil { + return nil, errors.Wrap(err, "failed to unpack oracle msg from authz msg") + } + + if clientMsg.SignedHeader.Header.ChainID != bs.host.ChainId() { + continue + } + + block, err := blockQuerier(clientMsg.SignedHeader.Header.Height + 1) + if err != nil { + return nil, errors.Wrap(err, "failed to query block") + } + + for sigIndex, signature := range clientMsg.SignedHeader.Commit.Signatures { + if blockSigIndex := slices.IndexFunc(block.Block.LastCommit.Signatures, func(blockSig cmttypes.CommitSig) bool { + if signature.ValidatorAddress != nil && + bytes.Equal(blockSig.ValidatorAddress.Bytes(), signature.ValidatorAddress) && + blockSig.Timestamp.Equal(signature.Timestamp) && + bytes.Equal(blockSig.Signature, signature.Signature) && + uint8(blockSig.BlockIDFlag) == uint8(signature.BlockIdFlag) { //nolint + return true + } + return false + }); blockSigIndex != -1 { + // assume that the length of the validator set is less than 65536 + if blockSigIndex >= 1<<16 { + return nil, errors.New("validator set length is greater than 65536") + } + + newSig := []byte{} + newSig = append(newSig, byte(blockSigIndex%(1<<8))) + newSig = append(newSig, byte(blockSigIndex>>8)) + + clientMsg.SignedHeader.Commit.Signatures[sigIndex].Signature = newSig + clientMsg.SignedHeader.Commit.Signatures[sigIndex].ValidatorAddress = []byte{} + clientMsg.SignedHeader.Commit.Signatures[sigIndex].Timestamp = time.Time{} + clientMsg.SignedHeader.Commit.Signatures[sigIndex].BlockIdFlag = 0 + } + } + + // empty validator set and trusted validators + clientMsg.ValidatorSet = &cmtproto.ValidatorSet{ + TotalVotingPower: clientMsg.ValidatorSet.TotalVotingPower, + } + clientMsg.TrustedValidators = &cmtproto.ValidatorSet{ + TotalVotingPower: clientMsg.TrustedValidators.TotalVotingPower, + } + + msgs[msgIndex], err = ibcclienttypes.NewMsgUpdateClient(msg.ClientId, clientMsg, msg.Signer) + if err != nil { + return nil, errors.Wrap(err, "failed to create new msg update client") + } + } + } + tx, err = txutils.ChangeMsgsFromTx(txConfig, tx, msgs) + if err != nil { + return nil, errors.Wrap(err, "failed to change msgs from tx") + } + convertedTxBytes, err := txutils.EncodeTx(txConfig, tx) + if err != nil { + return nil, errors.Wrap(err, "failed to encode tx") + } + pbb.Data.Txs[txIndex] = convertedTxBytes + } + return pbb, nil +} diff --git a/executor/batchsubmitter/data_conversion_test.go b/executor/batchsubmitter/data_conversion_test.go new file mode 100644 index 0000000..cc8dfe9 --- /dev/null +++ b/executor/batchsubmitter/data_conversion_test.go @@ -0,0 +1,598 @@ +package batchsubmitter + +import ( + "context" + "testing" + "time" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" + "github.com/initia-labs/opinit-bots/db" + "github.com/initia-labs/opinit-bots/node" + nodetypes "github.com/initia-labs/opinit-bots/node/types" + childprovider "github.com/initia-labs/opinit-bots/provider/child" + "github.com/initia-labs/opinit-bots/txutils" + "github.com/initia-labs/opinit-bots/types" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/cosmos/cosmos-sdk/client/tx" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibctmlightclients "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" +) + +func TestEmptyOracleData(t *testing.T) { + baseDB, err := db.NewMemDB() + require.NoError(t, err) + + batchDB := baseDB.WithPrefix([]byte("test_batch")) + appCodec, txConfig, err := childprovider.GetCodec("init") + require.NoError(t, err) + batchNode := node.NewTestNode(nodetypes.NodeConfig{}, batchDB, appCodec, txConfig, nil, nil) + + batchSubmitter := BatchSubmitter{node: batchNode} + + createAuthzMsg := func(t *testing.T, sender string, msgs []sdk.Msg) *authz.MsgExec { + msgsAny := make([]*cdctypes.Any, 0) + for _, msg := range msgs { + any, err := cdctypes.NewAnyWithValue(msg) + require.NoError(t, err) + msgsAny = append(msgsAny, any) + } + return &authz.MsgExec{ + Grantee: sender, + Msgs: msgsAny, + } + } + + cases := []struct { + name string + txs [][]sdk.Msg + expectedTxs [][]sdk.Msg + err bool + }{ + { + name: "0th oracle tx", + txs: [][]sdk.Msg{ + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + }, + }, + expectedTxs: [][]sdk.Msg{ + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte(""), Height: 3}, + }, + }, + err: false, + }, + { + name: "0th oracle tx with multiple msgs", + txs: [][]sdk.Msg{ + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + }, + }, + expectedTxs: [][]sdk.Msg{ + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + }, + }, + err: false, + }, + { + name: "1st oracle tx", + txs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + }, + }, + expectedTxs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, + }, + }, + err: false, + }, + { + name: "no txs", + txs: [][]sdk.Msg{}, + expectedTxs: [][]sdk.Msg{}, + err: false, + }, + { + name: "oracle authz tx", + txs: [][]sdk.Msg{ + { + createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}}), + }, + }, + expectedTxs: [][]sdk.Msg{ + { + createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte(""), Height: 3}}), + }, + }, + err: false, + }, + { + name: "authz tx with another msg", + txs: [][]sdk.Msg{ + { + createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}}), + }, + }, + expectedTxs: [][]sdk.Msg{ + { + createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}}), + }, + }, + err: false, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + txf := tx.Factory{}.WithChainID("test_chain").WithTxConfig(txConfig) + pbb := cmtproto.Block{ + Data: cmtproto.Data{ + Txs: [][]byte{}, + }, + } + + for _, msgs := range tc.txs { + txb, err := txf.BuildUnsignedTx(msgs...) + require.NoError(t, err) + txBytes, err := txutils.EncodeTx(txConfig, txb.GetTx()) + require.NoError(t, err) + pbb.Data.Txs = append(pbb.Data.Txs, txBytes) + } + + changedBlock, err := batchSubmitter.emptyOracleData(&pbb) + require.NoError(t, err) + + changedBlockTxs := changedBlock.Data.GetTxs() + require.Len(t, changedBlockTxs, len(tc.expectedTxs)) + + for i, txBytes := range changedBlockTxs { + tx, err := txutils.DecodeTx(txConfig, txBytes) + require.NoError(t, err) + for j, actual := range tx.GetMsgs() { + require.Equal(t, tc.expectedTxs[i][j].String(), actual.String()) + } + } + }) + } +} + +func TestEmptyUpdateClientData(t *testing.T) { + baseDB, err := db.NewMemDB() + require.NoError(t, err) + + batchDB := baseDB.WithPrefix([]byte("test_batch")) + appCodec, txConfig, err := childprovider.GetCodec("init") + require.NoError(t, err) + batchNode := node.NewTestNode(nodetypes.NodeConfig{}, batchDB, appCodec, txConfig, nil, nil) + + batchSubmitter := BatchSubmitter{node: batchNode} + + createUpdateClientMsg := func(t *testing.T, clientID string, header *ibctmlightclients.Header, sender string) *ibcclienttypes.MsgUpdateClient { + msg, err := ibcclienttypes.NewMsgUpdateClient(clientID, header, sender) + require.NoError(t, err) + return msg + } + + l1ChainId := "test-host-chain-id" + + cases := []struct { + name string + blocks []*cmttypes.Block + txs [][]sdk.Msg + expectedTxs [][]sdk.Msg + err bool + }{ + { + name: "same signatures", + blocks: []*cmttypes.Block{ //nolint + { + Header: cmttypes.Header{ + ChainID: l1ChainId, + Height: 6, + }, + LastCommit: &cmttypes.Commit{ + Signatures: []cmttypes.CommitSig{ + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address"), + Timestamp: time.Unix(0, 10000).UTC(), + Signature: []byte("signature"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address2"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature2"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address3"), + Timestamp: time.Unix(0, 10002).UTC(), + Signature: []byte("signature3"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address4"), + Timestamp: time.Unix(0, 10003).UTC(), + Signature: []byte("signature4"), + }, + }, + }, + }, + }, + txs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + createUpdateClientMsg(t, "clientid", &ibctmlightclients.Header{ + SignedHeader: &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ChainID: l1ChainId, + Height: 5, + }, + Commit: &cmtproto.Commit{ + Signatures: []cmtproto.CommitSig{ + { + ValidatorAddress: []byte("validator_address"), + Timestamp: time.Unix(0, 10000).UTC(), + Signature: []byte("signature"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte("validator_address4"), + Timestamp: time.Unix(0, 10003).UTC(), + Signature: []byte("signature4"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte("validator_address2"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature2"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte("validator_address3"), + Timestamp: time.Unix(0, 10002).UTC(), + Signature: []byte("signature3"), + BlockIdFlag: 2, + }, + }, + }, + }, + ValidatorSet: &cmtproto.ValidatorSet{ + Validators: []*cmtproto.Validator{ + { + Address: []byte("validator_address"), + VotingPower: 100, + }, + { + Address: []byte("validator_address2"), + VotingPower: 200, + }, + { + Address: []byte("validator_address3"), + VotingPower: 300, + }, + { + Address: []byte("validator_address4"), + VotingPower: 400, + }, + }, + }, + TrustedHeight: ibcclienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 10, + }, + TrustedValidators: &cmtproto.ValidatorSet{ + Validators: []*cmtproto.Validator{ + { + Address: []byte("validator_address"), + VotingPower: 100, + }, + { + Address: []byte("validator_address2"), + VotingPower: 200, + }, + { + Address: []byte("validator_address3"), + VotingPower: 300, + }, + { + Address: []byte("validator_address4"), + VotingPower: 400, + }, + }, + }, + }, "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5"), + }, + }, + expectedTxs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + createUpdateClientMsg(t, "clientid", &ibctmlightclients.Header{ + SignedHeader: &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ChainID: l1ChainId, + Height: 5, + }, + Commit: &cmtproto.Commit{ + Signatures: []cmtproto.CommitSig{ + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{0x00, 0x00}, + BlockIdFlag: 0, + }, + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{0x03, 0x00}, + BlockIdFlag: 0, + }, + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{0x01, 0x00}, + BlockIdFlag: 0, + }, + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{0x02, 0x00}, + BlockIdFlag: 0, + }, + }, + }, + }, + ValidatorSet: &cmtproto.ValidatorSet{}, + TrustedHeight: ibcclienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 10, + }, + TrustedValidators: &cmtproto.ValidatorSet{}, + }, "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5"), + }, + }, + err: false, + }, + { + name: "different signatures", + blocks: []*cmttypes.Block{ //nolint + { + Header: cmttypes.Header{ + ChainID: l1ChainId, + Height: 6, + }, + LastCommit: &cmttypes.Commit{ + Signatures: []cmttypes.CommitSig{ + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address"), + Timestamp: time.Unix(0, 10000).UTC(), + Signature: []byte("signature"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address2"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature2"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address3"), + Timestamp: time.Unix(0, 10002).UTC(), + Signature: []byte("signature3"), + }, + { + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: []byte("validator_address4"), + Timestamp: time.Unix(0, 10003).UTC(), + Signature: []byte("signature4"), + }, + }, + }, + }, + }, + txs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + createUpdateClientMsg(t, "clientid", &ibctmlightclients.Header{ + SignedHeader: &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ChainID: l1ChainId, + Height: 5, + }, + Commit: &cmtproto.Commit{ + Signatures: []cmtproto.CommitSig{ + { + ValidatorAddress: []byte("validator_address"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte("validator_address2"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature22"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{}, + BlockIdFlag: 0, + }, + { + ValidatorAddress: []byte("validator_address4"), + Timestamp: time.Unix(0, 10003).UTC(), + Signature: []byte("signature4"), + BlockIdFlag: 1, + }, + }, + }, + }, + ValidatorSet: &cmtproto.ValidatorSet{ + Validators: []*cmtproto.Validator{ + { + Address: []byte("validator_address"), + VotingPower: 100, + }, + { + Address: []byte("validator_address2"), + VotingPower: 200, + }, + { + Address: []byte("validator_address3"), + VotingPower: 300, + }, + { + Address: []byte("validator_address4"), + VotingPower: 400, + }, + }, + }, + TrustedHeight: ibcclienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 10, + }, + TrustedValidators: &cmtproto.ValidatorSet{ + Validators: []*cmtproto.Validator{ + { + Address: []byte("validator_address"), + VotingPower: 100, + }, + { + Address: []byte("validator_address2"), + VotingPower: 200, + }, + { + Address: []byte("validator_address3"), + VotingPower: 300, + }, + { + Address: []byte("validator_address4"), + VotingPower: 400, + }, + }, + }, + }, "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5"), + }, + }, + expectedTxs: [][]sdk.Msg{ + { + &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, + }, + { + createUpdateClientMsg(t, "clientid", &ibctmlightclients.Header{ + SignedHeader: &cmtproto.SignedHeader{ + Header: &cmtproto.Header{ + ChainID: l1ChainId, + Height: 5, + }, + Commit: &cmtproto.Commit{ + Signatures: []cmtproto.CommitSig{ + { + ValidatorAddress: []byte("validator_address"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte("validator_address2"), + Timestamp: time.Unix(0, 10001).UTC(), + Signature: []byte("signature22"), + BlockIdFlag: 2, + }, + { + ValidatorAddress: []byte{}, + Timestamp: time.Time{}, + Signature: []byte{}, + BlockIdFlag: 0, + }, + { + ValidatorAddress: []byte("validator_address4"), + Timestamp: time.Unix(0, 10003).UTC(), + Signature: []byte("signature4"), + BlockIdFlag: 1, + }, + }, + }, + }, + ValidatorSet: &cmtproto.ValidatorSet{}, + TrustedHeight: ibcclienttypes.Height{ + RevisionNumber: 0, + RevisionHeight: 10, + }, + TrustedValidators: &cmtproto.ValidatorSet{}, + }, "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5"), + }, + }, + err: false, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + mockHost := NewMockHost(nil, l1ChainId) + batchSubmitter.host = mockHost + + for _, block := range tc.blocks { + mockHost.SetBlock(block.Header.Height, block) + } + + txf := tx.Factory{}.WithChainID("test_chain").WithTxConfig(txConfig) + pbb := cmtproto.Block{ + Data: cmtproto.Data{ + Txs: [][]byte{}, + }, + } + + for _, msgs := range tc.txs { + txb, err := txf.BuildUnsignedTx(msgs...) + require.NoError(t, err) + txBytes, err := txutils.EncodeTx(txConfig, txb.GetTx()) + require.NoError(t, err) + pbb.Data.Txs = append(pbb.Data.Txs, txBytes) + } + + ctx := types.NewContext(context.TODO(), zap.NewNop(), "").WithPollingInterval(time.Millisecond) + changedBlock, err := batchSubmitter.emptyUpdateClientData(ctx, &pbb) + require.NoError(t, err) + + changedBlockTxs := changedBlock.Data.GetTxs() + require.Len(t, changedBlockTxs, len(tc.expectedTxs)) + + for i, txBytes := range changedBlockTxs { + tx, err := txutils.DecodeTx(txConfig, txBytes) + require.NoError(t, err) + for j, actual := range tx.GetMsgs() { + require.Equal(t, tc.expectedTxs[i][j].String(), actual.String()) + } + } + }) + } +} diff --git a/executor/batchsubmitter/handler.go b/executor/batchsubmitter/handler.go index b801c6f..e82b652 100644 --- a/executor/batchsubmitter/handler.go +++ b/executor/batchsubmitter/handler.go @@ -34,6 +34,11 @@ func (bs *BatchSubmitter) rawBlockHandler(ctx types.Context, args nodetypes.RawB return errors.Wrap(err, "failed to empty oracle data") } + pbb, err = bs.emptyUpdateClientData(ctx, pbb) + if err != nil { + return errors.Wrap(err, "failed to empty oracle data") + } + // convert block to bytes blockBytes, err := proto.Marshal(pbb) if err != nil { diff --git a/executor/batchsubmitter/oracle.go b/executor/batchsubmitter/oracle.go deleted file mode 100644 index 34e13bf..0000000 --- a/executor/batchsubmitter/oracle.go +++ /dev/null @@ -1,67 +0,0 @@ -package batchsubmitter - -import ( - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/authz" - - opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" - childprovider "github.com/initia-labs/opinit-bots/provider/child" - "github.com/initia-labs/opinit-bots/txutils" - "github.com/pkg/errors" -) - -// emptyOracleData converts the MsgUpdateOracle messages's data field to empty -// to decrease the size of the batch. -func (bs *BatchSubmitter) emptyOracleData(pbb *cmtproto.Block) (*cmtproto.Block, error) { - txs := pbb.Data.GetTxs() - if len(txs) == 0 { - return pbb, nil - } - txBytes := txs[0] - - txConfig := bs.node.GetTxConfig() - tx, err := txutils.DecodeTx(txConfig, txBytes) - if err != nil { - // ignore not registered tx in codec - return pbb, nil - } - - msgs := tx.GetMsgs() - // oracle tx has only one message - if len(msgs) != 1 { - return pbb, nil - } - - switch msg := msgs[0].(type) { - case *opchildtypes.MsgUpdateOracle: - msg.Data = []byte{} - case *authz.MsgExec: - if len(msg.Msgs) != 1 || msg.Msgs[0].TypeUrl != "/opinit.opchild.v1.MsgUpdateOracle" { - return pbb, nil - } - oracleMsg := &opchildtypes.MsgUpdateOracle{} - err = bs.node.Codec().UnpackAny(msg.Msgs[0], &oracleMsg) - if err != nil { - return nil, errors.Wrap(err, "failed to unpack oracle msg from authz msg") - } - oracleMsg.Data = []byte{} - msgs[0], err = childprovider.CreateAuthzMsg(msg.Grantee, oracleMsg) - if err != nil { - return nil, errors.Wrap(err, "failed to create authz msg") - } - } - - tx, err = txutils.ChangeMsgsFromTx(txConfig, tx, []sdk.Msg{msgs[0]}) - if err != nil { - return nil, errors.Wrap(err, "failed to change msgs from tx") - } - convertedTxBytes, err := txutils.EncodeTx(txConfig, tx) - if err != nil { - return nil, errors.Wrap(err, "failed to encode tx") - } - pbb.Data.Txs[0] = convertedTxBytes - - return pbb, nil -} diff --git a/executor/batchsubmitter/oracle_test.go b/executor/batchsubmitter/oracle_test.go deleted file mode 100644 index 39e2afe..0000000 --- a/executor/batchsubmitter/oracle_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package batchsubmitter - -import ( - "testing" - - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" - "github.com/initia-labs/opinit-bots/db" - "github.com/initia-labs/opinit-bots/node" - "github.com/initia-labs/opinit-bots/node/types" - childprovider "github.com/initia-labs/opinit-bots/provider/child" - "github.com/initia-labs/opinit-bots/txutils" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client/tx" - cdctypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/authz" -) - -func TestEmptyOracleData(t *testing.T) { - baseDB, err := db.NewMemDB() - require.NoError(t, err) - - batchDB := baseDB.WithPrefix([]byte("test_batch")) - appCodec, txConfig, err := childprovider.GetCodec("init") - require.NoError(t, err) - batchNode := node.NewTestNode(types.NodeConfig{}, batchDB, appCodec, txConfig, nil, nil) - - batchSubmitter := BatchSubmitter{node: batchNode} - - createAuthzMsg := func(t *testing.T, sender string, msgs []sdk.Msg) *authz.MsgExec { - msgsAny := make([]*cdctypes.Any, 0) - for _, msg := range msgs { - any, err := cdctypes.NewAnyWithValue(msg) - require.NoError(t, err) - msgsAny = append(msgsAny, any) - } - return &authz.MsgExec{ - Grantee: sender, - Msgs: msgsAny, - } - } - - cases := []struct { - name string - txs [][]sdk.Msg - expectedTxs [][]sdk.Msg - err bool - }{ - { - name: "0th oracle tx", - txs: [][]sdk.Msg{ - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - }, - }, - expectedTxs: [][]sdk.Msg{ - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte(""), Height: 3}, - }, - }, - err: false, - }, - { - name: "0th oracle tx with multiple msgs", - txs: [][]sdk.Msg{ - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - }, - }, - expectedTxs: [][]sdk.Msg{ - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - }, - }, - err: false, - }, - { - name: "1st oracle tx", - txs: [][]sdk.Msg{ - { - &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, - }, - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - }, - }, - expectedTxs: [][]sdk.Msg{ - { - &opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}, - }, - { - &opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}, - }, - }, - err: false, - }, - { - name: "no txs", - txs: [][]sdk.Msg{}, - expectedTxs: [][]sdk.Msg{}, - err: false, - }, - { - name: "oracle authz tx", - txs: [][]sdk.Msg{ - { - createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("oracle_data"), Height: 3}}), - }, - }, - expectedTxs: [][]sdk.Msg{ - { - createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgUpdateOracle{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte(""), Height: 3}}), - }, - }, - err: false, - }, - { - name: "authz tx with another msg", - txs: [][]sdk.Msg{ - { - createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}}), - }, - }, - expectedTxs: [][]sdk.Msg{ - { - createAuthzMsg(t, "init1z3689ct7pc72yr5an97nsj89dnlefydxwdhcv0", []sdk.Msg{&opchildtypes.MsgFinalizeTokenDeposit{Sender: "init1hrasklz3tr6s9rls4r8fjuf0k4zuha6w9rude5", Data: []byte("token_deposit_data"), Amount: sdk.NewInt64Coin("init", 10), Height: 5}}), - }, - }, - err: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - txf := tx.Factory{}.WithChainID("test_chain").WithTxConfig(txConfig) - pbb := cmtproto.Block{ - Data: cmtproto.Data{ - Txs: [][]byte{}, - }, - } - - for _, msgs := range tc.txs { - txb, err := txf.BuildUnsignedTx(msgs...) - require.NoError(t, err) - txBytes, err := txutils.EncodeTx(txConfig, txb.GetTx()) - require.NoError(t, err) - pbb.Data.Txs = append(pbb.Data.Txs, txBytes) - } - - changedBlock, err := batchSubmitter.emptyOracleData(&pbb) - require.NoError(t, err) - - changedBlockTxs := changedBlock.Data.GetTxs() - require.Len(t, changedBlockTxs, len(tc.expectedTxs)) - - for i, txBytes := range changedBlockTxs { - tx, err := txutils.DecodeTx(txConfig, txBytes) - require.NoError(t, err) - for j, actual := range tx.GetMsgs() { - require.Equal(t, tc.expectedTxs[i][j].String(), actual.String()) - } - } - }) - } -} diff --git a/executor/types/config.go b/executor/types/config.go index f620aa7..115839e 100644 --- a/executor/types/config.go +++ b/executor/types/config.go @@ -203,6 +203,7 @@ func (cfg *Config) Validate() error { func (cfg Config) L1NodeConfig() nodetypes.NodeConfig { nc := nodetypes.NodeConfig{ + ChainID: cfg.L1Node.ChainID, RPC: cfg.L1Node.RPCAddress, ProcessType: nodetypes.PROCESS_TYPE_DEFAULT, Bech32Prefix: cfg.L1Node.Bech32Prefix, @@ -223,6 +224,7 @@ func (cfg Config) L1NodeConfig() nodetypes.NodeConfig { func (cfg Config) L2NodeConfig() nodetypes.NodeConfig { nc := nodetypes.NodeConfig{ + ChainID: cfg.L2Node.ChainID, RPC: cfg.L2Node.RPCAddress, ProcessType: nodetypes.PROCESS_TYPE_DEFAULT, Bech32Prefix: cfg.L2Node.Bech32Prefix, @@ -243,6 +245,7 @@ func (cfg Config) L2NodeConfig() nodetypes.NodeConfig { func (cfg Config) DANodeConfig() nodetypes.NodeConfig { nc := nodetypes.NodeConfig{ + ChainID: cfg.DANode.ChainID, RPC: cfg.DANode.RPCAddress, ProcessType: nodetypes.PROCESS_TYPE_ONLY_BROADCAST, Bech32Prefix: cfg.DANode.Bech32Prefix, diff --git a/go.mod b/go.mod index 83eba74..cbe93c3 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/cosmos/cosmos-sdk v0.50.10 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.7.0 + github.com/cosmos/ibc-go/v8 v8.5.0 github.com/gofiber/fiber/v2 v2.52.5 github.com/initia-labs/OPinit v0.6.1 github.com/pkg/errors v0.9.1 @@ -60,7 +61,6 @@ require ( github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v1.2.0 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect - github.com/cosmos/ibc-go/v8 v8.5.0 // indirect github.com/cosmos/ics23/go v0.11.0 // indirect github.com/cosmos/interchain-security/v6 v6.0.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect diff --git a/node/types/config.go b/node/types/config.go index eac17c7..3cb6cf0 100644 --- a/node/types/config.go +++ b/node/types/config.go @@ -15,6 +15,10 @@ const ( ) type NodeConfig struct { + // ChainID is the chain ID of the chain. + ChainID string + + // RPC is the RPC address of the chain. RPC string // BlockProcessType is the type of block process. @@ -28,6 +32,10 @@ type NodeConfig struct { } func (nc NodeConfig) Validate() error { + if nc.ChainID == "" { + return fmt.Errorf("chain ID is empty") + } + if nc.RPC == "" { return fmt.Errorf("rpc is empty") } diff --git a/provider/child/child.go b/provider/child/child.go index 2dd7326..c6912d0 100644 --- a/provider/child/child.go +++ b/provider/child/child.go @@ -15,6 +15,8 @@ import ( opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibctmlightclients "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" "github.com/initia-labs/opinit-bots/keys" "github.com/initia-labs/opinit-bots/merkle" merkletypes "github.com/initia-labs/opinit-bots/merkle/types" @@ -22,7 +24,6 @@ import ( btypes "github.com/initia-labs/opinit-bots/node/broadcaster/types" nodetypes "github.com/initia-labs/opinit-bots/node/types" "github.com/initia-labs/opinit-bots/types" - "github.com/pkg/errors" ) @@ -93,6 +94,8 @@ func GetCodec(bech32Prefix string) (codec.Codec, client.TxConfig, error) { return keys.CreateCodec([]keys.RegisterInterfaces{ auth.AppModuleBasic{}.RegisterInterfaces, authz.RegisterInterfaces, + ibcclienttypes.RegisterInterfaces, + ibctmlightclients.RegisterInterfaces, opchild.AppModuleBasic{}.RegisterInterfaces, }) } diff --git a/provider/host/host.go b/provider/host/host.go index b29e3eb..4af4a13 100644 --- a/provider/host/host.go +++ b/provider/host/host.go @@ -116,6 +116,10 @@ func (b BaseHost) BridgeId() uint64 { return b.bridgeInfo.BridgeId } +func (b BaseHost) ChainId() string { + return b.cfg.ChainID +} + func (b BaseHost) OracleEnabled() bool { return b.bridgeInfo.BridgeConfig.OracleEnabled } diff --git a/provider/host/query.go b/provider/host/query.go index 2fb3bbc..3ee37d9 100644 --- a/provider/host/query.go +++ b/provider/host/query.go @@ -7,6 +7,7 @@ import ( "strings" "time" + coretypes "github.com/cometbft/cometbft/rpc/core/types" query "github.com/cosmos/cosmos-sdk/types/query" ophosttypes "github.com/initia-labs/OPinit/x/ophost/types" @@ -195,3 +196,7 @@ func (b BaseHost) QueryDepositTxHeight(botCtx types.Context, bridgeId uint64, l1 } return 0, nil } + +func (b BaseHost) QueryBlock(ctx context.Context, height int64) (*coretypes.ResultBlock, error) { + return b.node.GetRPCClient().Block(ctx, &height) +}