diff --git a/client/bytes/bytes.go b/client/bytes/bytes.go new file mode 100644 index 000000000..933be5b76 --- /dev/null +++ b/client/bytes/bytes.go @@ -0,0 +1,74 @@ +// FROM: COMETBFT +// IBC-Go also uses this package. Maybe the SDK in v2 upstreams this? + +package bytes + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/cometbft/cometbft/libs/bytes" +) + +// HexBytes enables HEX-encoding for json/encoding. +type HexBytes []byte + +// Marshal needed for protobuf compatibility +func (bz HexBytes) Marshal() ([]byte, error) { + return bz, nil +} + +// Unmarshal needed for protobuf compatibility +func (bz *HexBytes) Unmarshal(data []byte) error { + *bz = data + return nil +} + +// This is the point of Bytes. +func (bz HexBytes) MarshalJSON() ([]byte, error) { + s := strings.ToUpper(hex.EncodeToString(bz)) + jbz := make([]byte, len(s)+2) + jbz[0] = '"' + copy(jbz[1:], s) + jbz[len(jbz)-1] = '"' + return jbz, nil +} + +// This is the point of Bytes. +func (bz *HexBytes) UnmarshalJSON(data []byte) error { + if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { + return fmt.Errorf("invalid hex string: %s", data) + } + bz2, err := hex.DecodeString(string(data[1 : len(data)-1])) + if err != nil { + return err + } + *bz = bz2 + return nil +} + +// Bytes fulfills various interfaces in light-client, etc... +func (bz HexBytes) Bytes() []byte { + return bz +} + +func (bz HexBytes) String() string { + return strings.ToUpper(hex.EncodeToString(bz)) +} + +// Format writes either address of 0th element in a slice in base 16 notation, +// with leading 0x (%p), or casts HexBytes to bytes and writes as hexadecimal +// string to s. +func (bz HexBytes) Format(s fmt.State, verb rune) { + switch verb { + case 'p': + s.Write([]byte(fmt.Sprintf("%p", bz))) + default: + s.Write([]byte(fmt.Sprintf("%X", []byte(bz)))) + } +} + +func ConvertCometBFTToHexBytes(cbft bytes.HexBytes) HexBytes { + return HexBytes(cbft) +} diff --git a/client/cometbft_consensus.go b/client/cometbft_consensus.go index 16322398b..51a670889 100644 --- a/client/cometbft_consensus.go +++ b/client/cometbft_consensus.go @@ -9,6 +9,7 @@ import ( rpcclient "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + rbytes "github.com/cosmos/relayer/v2/client/bytes" ) // ConsensusRelayerI is the itnerface we will use across the relayer so we can swap out the underlying consensus engine client. @@ -27,22 +28,28 @@ func (r CometRPCClient) GetBlockTime(ctx context.Context, height uint64) (time.T } // GetBlockResults implements ConsensusRelayerI. -func (r CometRPCClient) GetBlockResults(ctx context.Context, height uint64) (*coretypes.ResultBlockResults, error) { +func (r CometRPCClient) GetBlockResults(ctx context.Context, height uint64) (*BlockResults, error) { h := int64(height) br, err := r.BlockResults(ctx, &h) if err != nil { return nil, fmt.Errorf("failed to get block results: %w", err) } - return br, nil + return &BlockResults{ + TxsResults: br.TxsResults, + FinalizeBlockEvents: br.FinalizeBlockEvents, + }, nil } // GetABCIQuery implements ConsensusRelayerI. -func (r CometRPCClient) GetABCIQuery(ctx context.Context, queryPath string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { +func (r CometRPCClient) GetABCIQuery(ctx context.Context, queryPath string, data bytes.HexBytes) (*ABCIQueryResponse, error) { resp, err := r.ABCIQuery(ctx, queryPath, data) if err != nil { return nil, fmt.Errorf("failed to get ABCI query: %w", err) } - return resp, nil + return &ABCIQueryResponse{ + Code: resp.Response.Code, + Value: resp.Response.Value, + }, nil } // GetTx implements ConsensusRelayerI. @@ -51,16 +58,26 @@ func (r CometRPCClient) GetTx(ctx context.Context, hash []byte, prove bool) (*co if err != nil { return nil, fmt.Errorf("failed to get tx: %w", err) } + // return &Transaction{ + // Height: uint64(resp.Height), + // TxHash: resp.Hash, + // Code: resp.TxResult.Code, + // Data: string(resp.TxResult.Data), + // Events: resp.TxResult.Events, + // }, nil return resp, nil } // GetTxSearch implements ConsensusRelayerI. -func (r CometRPCClient) GetTxSearch(ctx context.Context, query string, prove bool, page *int, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) { +func (r CometRPCClient) GetTxSearch(ctx context.Context, query string, prove bool, page *int, perPage *int, orderBy string) (*ResultTxSearch, error) { resp, err := r.TxSearch(ctx, query, prove, page, perPage, orderBy) if err != nil { return nil, fmt.Errorf("failed to get tx search: %w", err) } - return resp, nil + return &ResultTxSearch{ + Txs: resp.Txs, + TotalCount: resp.TotalCount, + }, nil } // GetBlockSearch implements ConsensusRelayerI. @@ -82,30 +99,55 @@ func (r CometRPCClient) GetCommit(ctx context.Context, height *int64) (*coretype } // GetValidators implements ConsensusRelayerI. -func (r CometRPCClient) GetValidators(ctx context.Context, height *int64, page *int, perPage *int) (*coretypes.ResultValidators, error) { +func (r CometRPCClient) GetValidators(ctx context.Context, height *int64, page *int, perPage *int) (*ResultValidators, error) { v, err := r.Validators(ctx, height, page, perPage) if err != nil { return nil, fmt.Errorf("failed to get validators: %w", err) } - return v, nil + + vals := make([]*tmtypes.Validator, len(v.Validators)) + for i, val := range v.Validators { + vals[i] = &tmtypes.Validator{ + Address: val.Address, + PubKey: val.PubKey, + VotingPower: val.VotingPower, + ProposerPriority: val.ProposerPriority, + } + } + + return &ResultValidators{ + Validators: vals, + }, nil } // DoBroadcastTxAsync implements ConsensusRelayerI. -func (r CometRPCClient) DoBroadcastTxAsync(ctx context.Context, tx tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { +func (r CometRPCClient) DoBroadcastTxAsync(ctx context.Context, tx tmtypes.Tx) (*ResultBroadcastTx, error) { b, err := r.BroadcastTxAsync(ctx, tx) if err != nil { return nil, fmt.Errorf("failed to broadcast tx async: %w", err) } - return b, nil + return &ResultBroadcastTx{ + Code: b.Code, + Data: rbytes.ConvertCometBFTToHexBytes(b.Data), + Log: b.Log, + Codespace: b.Codespace, + Hash: rbytes.ConvertCometBFTToHexBytes(b.Hash), + }, nil } // DoBroadcastTxSync implements ConsensusRelayerI. -func (r CometRPCClient) DoBroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { +func (r CometRPCClient) DoBroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*ResultBroadcastTx, error) { b, err := r.BroadcastTxSync(ctx, tx) if err != nil { return nil, fmt.Errorf("failed to broadcast tx sync: %w", err) } - return b, nil + return &ResultBroadcastTx{ + Code: b.Code, + Data: rbytes.ConvertCometBFTToHexBytes(b.Data), + Log: b.Log, + Codespace: b.Codespace, + Hash: rbytes.ConvertCometBFTToHexBytes(b.Hash), + }, nil } // GetABCIQueryWithOptions implements ConsensusRelayerI. diff --git a/client/i.go b/client/i.go index e50e288e6..a59665743 100644 --- a/client/i.go +++ b/client/i.go @@ -2,12 +2,17 @@ package client import ( "context" + "strings" "time" - "github.com/cometbft/cometbft/libs/bytes" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto" rpcclient "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + + bytes "github.com/cometbft/cometbft/libs/bytes" + rbytes "github.com/cosmos/relayer/v2/client/bytes" ) // make this so we can use the cometbft & gordian for this. @@ -17,21 +22,26 @@ import ( type ConsensusRelayerI interface { // DONE: (gordian /Block, just returns time.Now() for now. Need to impl proposer setting time) GetBlockTime(ctx context.Context, height uint64) (time.Time, error) // resultBlock.Block.Time (return the header annotation time.Time) - - GetStatus(ctx context.Context) (*Status, error) // TODO: - - GetBlockResults(ctx context.Context, height uint64) (*coretypes.ResultBlockResults, error) // in (d *Driver) handleFinalization - GetABCIQuery(ctx context.Context, queryPath string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) // err != nil || resp.Response.Code != 0 // TODO: make this through baseapp `(app *BaseApp) Query` now? the store should handle - - GetTx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error) // resp (Events), err != nil - does this need its own tm store? or does the manager have context to this - + GetStatus(ctx context.Context) (*Status, error) + GetBlockResults(ctx context.Context, height uint64) (*BlockResults, error) // in (d *Driver) handleFinalization + GetABCIQuery(ctx context.Context, queryPath string, data bytes.HexBytes) (*ABCIQueryResponse, error) // err != nil || resp.Response.Code != 0 // TODO: make this through baseapp `(app *BaseApp) Query` now? the store should handle + GetValidators( + ctx context.Context, + height *int64, + page, perPage *int, + ) (*ResultValidators, error) GetTxSearch( ctx context.Context, query string, prove bool, page, perPage *int, orderBy string, - ) (*coretypes.ResultTxSearch, error) + ) (*ResultTxSearch, error) + DoBroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*ResultBroadcastTx, error) + DoBroadcastTxAsync(ctx context.Context, tx tmtypes.Tx) (*ResultBroadcastTx, error) + + // TODO: migrate with v2 + GetTx(ctx context.Context, hash []byte, prove bool) (*coretypes.ResultTx, error) // resp (Events), err != nil - does this need its own tm store? or does the manager have context to this GetBlockSearch( ctx context.Context, @@ -41,21 +51,12 @@ type ConsensusRelayerI interface { ) (*coretypes.ResultBlockSearch, error) GetCommit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) - GetValidators( - ctx context.Context, - height *int64, - page, perPage *int, - ) (*coretypes.ResultValidators, error) - GetABCIQueryWithOptions( ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions, ) (*coretypes.ResultABCIQuery, error) - - DoBroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) - DoBroadcastTxAsync(ctx context.Context, tx tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) } // Not sure if we can re-use the cometbft type. Just trying to get exactly what we need at minimum @@ -68,3 +69,55 @@ type Status struct { CatchingUp bool LatestBlockHeight uint64 } + +type BlockResults struct { + // TODO: ideally we get off of this into our own internal type. Then the ConsensusRelayerI can have methods to convert + FinalizeBlockEvents []abci.Event `json:"finalize_block_events"` + TxsResults []*abci.ExecTxResult `json:"txs_results"` +} + +type ABCIQueryResponse struct { + Code uint32 `json:"code,omitempty"` + Value []byte `json:"value,omitempty"` +} + +func (q ABCIQueryResponse) ValueCleaned() string { + // The response value contains the data link escape control character which must be removed before parsing. + return strings.ReplaceAll(strings.TrimSpace(string(q.Value)), "\u0010", "") +} + +// TODO: can't do this yet as the cosmos-sdk side in v0.50 is tied to cometbft +// type Transaction struct { +// Height uint64 +// TxHash []byte +// Code uint32 +// Data string +// Events []abci.Event // TODO: []provider.RelayerEvent +// Tx cmtypes.Tx `json:"tx"` +// } + +// coretypes.ResultTxSearch +type ResultTxSearch struct { + Txs []*coretypes.ResultTx `json:"txs"` + TotalCount int `json:"total_count"` +} + +type ResultValidators struct { + Validators []*tmtypes.Validator `json:"validators"` + // Validators []Validator // TODO: requires some helper methods on the gordian side for the query to update set stuff +} + +type Validator struct { + Address crypto.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + VotingPower int64 `json:"voting_power"` + ProposerPriority int64 `json:"proposer_priority"` +} + +type ResultBroadcastTx struct { + Code uint32 `json:"code"` + Data rbytes.HexBytes `json:"data"` + Log string `json:"log"` + Codespace string `json:"codespace"` + Hash rbytes.HexBytes `json:"hash"` +} diff --git a/relayer/chains/cosmos/cosmos_chain_processor.go b/relayer/chains/cosmos/cosmos_chain_processor.go index f19ff1c05..b6245a66f 100644 --- a/relayer/chains/cosmos/cosmos_chain_processor.go +++ b/relayer/chains/cosmos/cosmos_chain_processor.go @@ -8,7 +8,6 @@ import ( "time" "github.com/avast/retry-go/v4" - coretypes "github.com/cometbft/cometbft/rpc/core/types" sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" conntypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" @@ -394,7 +393,7 @@ func (ccp *CosmosChainProcessor) queryCycle(ctx context.Context, persistence *qu for i := persistence.latestQueriedBlock + 1; i <= persistence.latestHeight; i++ { var ( eg errgroup.Group - blockRes *coretypes.ResultBlockResults + blockRes *relayerclient.BlockResults ibcHeader provider.IBCHeader ) diff --git a/relayer/chains/cosmos/fee_market.go b/relayer/chains/cosmos/fee_market.go index 99a90f0e5..d2e96bb50 100644 --- a/relayer/chains/cosmos/fee_market.go +++ b/relayer/chains/cosmos/fee_market.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "regexp" - "strings" sdkmath "cosmossdk.io/math" "go.uber.org/zap" @@ -33,14 +32,11 @@ func (cc *CosmosProvider) DynamicFee(ctx context.Context) string { // This is currently hardcoded to only work on Osmosis. func (cc *CosmosProvider) QueryBaseFee(ctx context.Context) (string, error) { resp, err := cc.ConsensusClient.GetABCIQuery(ctx, queryPath, nil) - if err != nil || resp.Response.Code != 0 { + if err != nil || resp.Code != 0 { return "", err } - // The response value contains the data link escape control character which must be removed before parsing. - cleanedString := strings.ReplaceAll(strings.TrimSpace(string(resp.Response.Value)), "\u0010", "") - - decFee, err := sdkmath.LegacyNewDecFromStr(cleanedString) + decFee, err := sdkmath.LegacyNewDecFromStr(resp.ValueCleaned()) if err != nil { return "", err } diff --git a/relayer/chains/cosmos/query.go b/relayer/chains/cosmos/query.go index d20c56626..571887b0a 100644 --- a/relayer/chains/cosmos/query.go +++ b/relayer/chains/cosmos/query.go @@ -121,7 +121,7 @@ func (cc *CosmosProvider) QueryTx(ctx context.Context, hashHex string) (*provide return nil, err } - // TODO: why proove here if we don't actually use it? does it change the events? + // TODO: why prove here if we don't actually use it? does it change the events? resp, err := cc.ConsensusClient.GetTx(ctx, hash, true) if err != nil { return nil, err @@ -130,7 +130,7 @@ func (cc *CosmosProvider) QueryTx(ctx context.Context, hashHex string) (*provide events := parseEventsFromResponseDeliverTx(resp.TxResult.Events) return &provider.RelayerTxResponse{ - Height: resp.Height, + Height: int64(resp.Height), TxHash: string(hash), Code: resp.TxResult.Code, Data: string(resp.TxResult.Data), diff --git a/relayer/chains/cosmos/tx.go b/relayer/chains/cosmos/tx.go index dbfb20ded..68160be48 100644 --- a/relayer/chains/cosmos/tx.go +++ b/relayer/chains/cosmos/tx.go @@ -17,7 +17,6 @@ import ( "cosmossdk.io/store/rootmulti" "github.com/avast/retry-go/v4" abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/bytes" "github.com/cometbft/cometbft/light" client2 "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" @@ -42,6 +41,8 @@ import ( ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" tmclient "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost" + relayerclient "github.com/cosmos/relayer/v2/client" + rbytes "github.com/cosmos/relayer/v2/client/bytes" strideicqtypes "github.com/cosmos/relayer/v2/relayer/chains/cosmos/stride" "github.com/cosmos/relayer/v2/relayer/provider" "go.uber.org/zap" @@ -231,7 +232,7 @@ func (cc *CosmosProvider) SubmitTxAwaitResponse(ctx context.Context, msgs []sdk. } // Get the TX by hash, waiting for it to be included in a block -func (cc *CosmosProvider) AwaitTx(txHash bytes.HexBytes, timeout time.Duration) (*txtypes.GetTxResponse, error) { +func (cc *CosmosProvider) AwaitTx(txHash rbytes.HexBytes, timeout time.Duration) (*txtypes.GetTxResponse, error) { var txByHash *txtypes.GetTxResponse var txLookupErr error startTime := time.Now() @@ -263,7 +264,7 @@ func (cc *CosmosProvider) AwaitTx(txHash bytes.HexBytes, timeout time.Duration) // sent and executed successfully is returned. // // feegranterKey - key name of the address set as the feegranter, empty string will not feegrant -func (cc *CosmosProvider) SendMsgsWith(ctx context.Context, msgs []sdk.Msg, memo string, gas uint64, signingKey string, feegranterKey string) (*coretypes.ResultBroadcastTx, error) { +func (cc *CosmosProvider) SendMsgsWith(ctx context.Context, msgs []sdk.Msg, memo string, gas uint64, signingKey string, feegranterKey string) (*relayerclient.ResultBroadcastTx, error) { sdkConfigMutex.Lock() sdkConf := sdk.GetConfig() sdkConf.SetBech32PrefixForAccount(cc.PCfg.AccountPrefix, cc.PCfg.AccountPrefix+"pub")