diff --git a/migrate/rest_v0_3/rest.go b/migrate/rest_v0_3/rest.go index ce65d3c0..c6f3d04e 100644 --- a/migrate/rest_v0_3/rest.go +++ b/migrate/rest_v0_3/rest.go @@ -3,6 +3,7 @@ package rest_v0_3 import ( "fmt" "net/http" + "strings" "github.com/gorilla/mux" @@ -13,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/auth/types" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" @@ -22,6 +24,9 @@ import ( v18de63auth "github.com/kava-labs/kava/migrate/v0_8/sdk/auth/v18de63" v18de63supply "github.com/kava-labs/kava/migrate/v0_8/sdk/supply/v18de63" + v18de63sdk "github.com/kava-labs/kava/migrate/v0_8/sdk/types" + v032tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32" + v032tendermintrpc "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32/rpccore" valvesting "github.com/kava-labs/kava/x/validator-vesting" v0_3valvesting "github.com/kava-labs/kava/x/validator-vesting/legacy/v0_3" ) @@ -30,13 +35,16 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { s := r.PathPrefix("/v0_3").Subrouter() s.HandleFunc("/node_info", rpc.NodeInfoRequestHandlerFn(cliCtx)).Methods("GET") - s.HandleFunc( - "/auth/accounts/{address}", QueryAccountRequestHandlerFn(cliCtx), - ).Methods("GET") - s.HandleFunc("/txs/{hash}", authrest.QueryTxRequestHandlerFn(cliCtx)).Methods("GET") - // r.HandleFunc("/txs", QueryTxsRequestHandlerFn(cliCtx)).Methods("GET") // assume they don't need GET here + + s.HandleFunc("/auth/accounts/{address}", QueryAccountRequestHandlerFn(cliCtx)).Methods("GET") + + s.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cliCtx)).Methods("GET") + // r.HandleFunc("/txs", QueryTxsRequestHandlerFn(cliCtx)).Methods("GET") // TODO does trust wallet query txs? s.HandleFunc("/txs", authrest.BroadcastTxRequest(cliCtx)).Methods("POST") + s.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(cliCtx)).Methods("GET") + + // These endpoints are unchanged between cosmos v18de63 and v0.38.4, but can't import private methods so copy and pasting handler methods. // Get all delegations from a delegator s.HandleFunc( "/staking/delegators/{delegatorAddr}/delegations", @@ -57,6 +65,73 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { } +// REST handler to get the latest block +func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + output, err := getBlock(cliCtx, nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + rest.PostProcessResponseBare(w, cliCtx, output) + } +} + +func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { + // get the node + node, err := cliCtx.GetNode() + if err != nil { + return nil, err + } + + res, err := node.Block(height) + if err != nil { + return nil, err + } + + // Convert block to old type + header := v032tendermint.Header{ + Version: v032tendermint.Consensus{ + Block: v032tendermint.Protocol(res.Block.Header.Version.Block), + App: v032tendermint.Protocol(res.Block.Header.Version.App), + }, + ChainID: res.Block.Header.ChainID, + Height: res.Block.Header.Height, + Time: res.Block.Header.Time, + NumTxs: 0, // trust wallet doesn't use this field + TotalTxs: 0, // trust wallet doesn't use this field + + LastBlockID: res.Block.Header.LastBlockID, + + LastCommitHash: res.Block.Header.LastCommitHash, + DataHash: res.Block.Header.DataHash, + ValidatorsHash: res.Block.Header.ValidatorsHash, + NextValidatorsHash: res.Block.Header.NextValidatorsHash, + ConsensusHash: res.Block.Header.ConsensusHash, + AppHash: res.Block.Header.AppHash, + LastResultsHash: res.Block.Header.LastResultsHash, + EvidenceHash: res.Block.Header.EvidenceHash, + ProposerAddress: res.Block.Header.ProposerAddress, + } + block := v032tendermint.Block{ + Header: header, + Data: res.Block.Data, + Evidence: res.Block.Evidence, + LastCommit: nil, // trust wallet doesn't need to access commit info + } + blockMeta := v032tendermint.BlockMeta{ + BlockID: res.BlockID, + Header: header, + } + oldResponse := v032tendermintrpc.ResultBlock{ + Block: &block, + BlockMeta: &blockMeta, + } + + return codec.Cdc.MarshalJSON(oldResponse) +} + // QueryAccountRequestHandlerFn handle auth/accounts queries // This function is identical to v0.8 except the queried account is cast to the v0.3 account type so it marshals in the old format. func QueryAccountRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { @@ -100,6 +175,61 @@ func QueryAccountRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } +// QueryTxRequestHandlerFn implements a REST handler that queries a transaction +// by hash in a committed block. +func QueryTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + hashHexStr := vars["hash"] + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + output, err := utils.QueryTx(cliCtx, hashHexStr) + if err != nil { + if strings.Contains(err.Error(), hashHexStr) { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + // convert v0.8 TxResponse to a v0.3 Tx Response + oldOutput := rollbackTxResponseType(output) + if oldOutput.Empty() { + rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr)) + } + + rest.PostProcessResponseBare(w, cliCtx, oldOutput) + } +} + +func rollbackTxResponseType(response sdk.TxResponse) v18de63sdk.TxResponse { + events := sdk.StringEvents{} + for _, msgLog := range response.Logs { + events = append(events, msgLog.Events...) + } + return v18de63sdk.TxResponse{ + Height: response.Height, + TxHash: response.TxHash, + Codespace: response.Codespace, + Code: response.Code, + Data: response.Data, + RawLog: response.RawLog, + Logs: nil, // trust wallet doesn't use logs, so leaving them out + Info: response.Info, + GasWanted: response.GasWanted, + GasUsed: response.GasUsed, + Tx: response.Tx, + Timestamp: response.Timestamp, + Events: events, + } + +} + func makeCodecV03() *codec.Codec { v0_3Codec := codec.New() codec.RegisterCrypto(v0_3Codec) diff --git a/migrate/v0_8/sdk/types/result.go b/migrate/v0_8/sdk/types/result.go new file mode 100644 index 00000000..bfa55253 --- /dev/null +++ b/migrate/v0_8/sdk/types/result.go @@ -0,0 +1,42 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// TxResponse defines a structure containing relevant tx data and metadata. The +// tags are stringified and the log is JSON decoded. +type TxResponse struct { + Height int64 `json:"height"` + TxHash string `json:"txhash"` + Code uint32 `json:"code,omitempty"` + Data string `json:"data,omitempty"` + RawLog string `json:"raw_log,omitempty"` + Logs ABCIMessageLogs `json:"logs,omitempty"` + Info string `json:"info,omitempty"` + GasWanted int64 `json:"gas_wanted,omitempty"` + GasUsed int64 `json:"gas_used,omitempty"` + Codespace string `json:"codespace,omitempty"` + Tx sdk.Tx `json:"tx,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + Events sdk.StringEvents `json:"events,omitempty"` +} + +// Empty returns true if the response is empty +func (r TxResponse) Empty() bool { + return r.TxHash == "" && r.Logs == nil +} + +// ABCIMessageLogs represents a slice of ABCIMessageLog. +type ABCIMessageLogs []ABCIMessageLog + +// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +type ABCIMessageLog struct { + MsgIndex uint16 `json:"msg_index"` + Success bool `json:"success"` + Log string `json:"log"` + + // Events contains a slice of Event objects that were emitted during some + // execution. + Events sdk.StringEvents `json:"events"` +} diff --git a/migrate/v0_8/tendermint/v0_32/block.go b/migrate/v0_8/tendermint/v0_32/block.go new file mode 100644 index 00000000..e2f9710d --- /dev/null +++ b/migrate/v0_8/tendermint/v0_32/block.go @@ -0,0 +1,52 @@ +package v032 + +import ( + "sync" + "time" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmtypes "github.com/tendermint/tendermint/types" +) + +// Block defines the atomic unit of a Tendermint blockchain. +type Block struct { + mtx sync.Mutex + Header `json:"header"` + Data tmtypes.Data `json:"data"` + Evidence tmtypes.EvidenceData `json:"evidence"` + LastCommit *Commit `json:"last_commit"` // not using for trust wallet +} + +// Header defines the structure of a Tendermint block header. +type Header struct { + // basic block info + Version Consensus `json:"version"` + ChainID string `json:"chain_id"` + Height int64 `json:"height"` + Time time.Time `json:"time"` + NumTxs int64 `json:"num_txs"` + TotalTxs int64 `json:"total_txs"` + + // prev block info + LastBlockID tmtypes.BlockID `json:"last_block_id"` + + // hashes of block data + LastCommitHash tmbytes.HexBytes `json:"last_commit_hash"` // commit from validators from the last block + DataHash tmbytes.HexBytes `json:"data_hash"` // transactions + + // hashes from the app output from the prev block + ValidatorsHash tmbytes.HexBytes `json:"validators_hash"` // validators for the current block + NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators for the next block + ConsensusHash tmbytes.HexBytes `json:"consensus_hash"` // consensus params for current block + AppHash tmbytes.HexBytes `json:"app_hash"` // state after txs from the previous block + // root hash of all results from the txs from the previous block + LastResultsHash tmbytes.HexBytes `json:"last_results_hash"` + + // consensus info + EvidenceHash tmbytes.HexBytes `json:"evidence_hash"` // evidence included in the block + ProposerAddress tmtypes.Address `json:"proposer_address"` // original proposer of the block +} + +// Commit contains the evidence that a block was committed by a set of validators. +type Commit struct { +} diff --git a/migrate/v0_8/tendermint/v0_32/block_meta.go b/migrate/v0_8/tendermint/v0_32/block_meta.go new file mode 100644 index 00000000..a902e185 --- /dev/null +++ b/migrate/v0_8/tendermint/v0_32/block_meta.go @@ -0,0 +1,11 @@ +package v032 + +import ( + tmtypes "github.com/tendermint/tendermint/types" +) + +// BlockMeta contains meta information about a block - namely, it's ID and Header. +type BlockMeta struct { + BlockID tmtypes.BlockID `json:"block_id"` // the block hash and partsethash + Header Header `json:"header"` // The block's Header +} diff --git a/migrate/v0_8/tendermint/v0_32/rpccore/responses.go b/migrate/v0_8/tendermint/v0_32/rpccore/responses.go new file mode 100644 index 00000000..4a36105e --- /dev/null +++ b/migrate/v0_8/tendermint/v0_32/rpccore/responses.go @@ -0,0 +1,11 @@ +package rpccore + +import ( + types "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32" +) + +// Single block (with meta) +type ResultBlock struct { + BlockMeta *types.BlockMeta `json:"block_meta"` + Block *types.Block `json:"block"` +} diff --git a/migrate/v0_8/tendermint/v0_32/version.go b/migrate/v0_8/tendermint/v0_32/version.go new file mode 100644 index 00000000..bd7873c3 --- /dev/null +++ b/migrate/v0_8/tendermint/v0_32/version.go @@ -0,0 +1,12 @@ +package v032 + +// Protocol is used for implementation agnostic versioning. +type Protocol uint64 + +// Consensus captures the consensus rules for processing a block in the blockchain, +// including all blockchain data structures and the rules of the application's +// state transition machine. +type Consensus struct { + Block Protocol `json:"block"` + App Protocol `json:"app"` +}