package types import ( "encoding/hex" "encoding/json" "fmt" "math" "strings" "github.com/gogo/protobuf/proto" yaml "gopkg.in/yaml.v2" abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" ) var cdc = codec.NewLegacyAmino() func (gi GasInfo) String() string { bz, _ := yaml.Marshal(gi) return string(bz) } func (r Result) String() string { bz, _ := yaml.Marshal(r) return string(bz) } func (r Result) GetEvents() Events { events := make(Events, len(r.Events)) for i, e := range r.Events { events[i] = Event(e) } return events } // ABCIMessageLogs represents a slice of ABCIMessageLog. type ABCIMessageLogs []ABCIMessageLog func NewABCIMessageLog(i uint32, log string, events Events) ABCIMessageLog { return ABCIMessageLog{ MsgIndex: i, Log: log, Events: StringifyEvents(events.ToABCIEvents()), } } // String implements the fmt.Stringer interface for the ABCIMessageLogs type. func (logs ABCIMessageLogs) String() (str string) { if logs != nil { raw, err := cdc.MarshalJSON(logs) if err == nil { str = string(raw) } } return str } // NewResponseResultTx returns a TxResponse given a ResultTx from tendermint func NewResponseResultTx(res *ctypes.ResultTx, anyTx *codectypes.Any, timestamp string) *TxResponse { if res == nil { return nil } parsedLogs, _ := ParseABCILogs(res.TxResult.Log) return &TxResponse{ TxHash: res.Hash.String(), Height: res.Height, Codespace: res.TxResult.Codespace, Code: res.TxResult.Code, Data: strings.ToUpper(hex.EncodeToString(res.TxResult.Data)), RawLog: res.TxResult.Log, Logs: parsedLogs, Info: res.TxResult.Info, GasWanted: res.TxResult.GasWanted, GasUsed: res.TxResult.GasUsed, Tx: anyTx, Timestamp: timestamp, } } // NewResponseFormatBroadcastTxCommit returns a TxResponse given a // ResultBroadcastTxCommit from tendermint. func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { return nil } if !res.CheckTx.IsOK() { return newTxResponseCheckTx(res) } return newTxResponseDeliverTx(res) } func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { return nil } var txHash string if res.Hash != nil { txHash = res.Hash.String() } parsedLogs, _ := ParseABCILogs(res.CheckTx.Log) return &TxResponse{ Height: res.Height, TxHash: txHash, Codespace: res.CheckTx.Codespace, Code: res.CheckTx.Code, Data: strings.ToUpper(hex.EncodeToString(res.CheckTx.Data)), RawLog: res.CheckTx.Log, Logs: parsedLogs, Info: res.CheckTx.Info, GasWanted: res.CheckTx.GasWanted, GasUsed: res.CheckTx.GasUsed, } } func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { return nil } var txHash string if res.Hash != nil { txHash = res.Hash.String() } parsedLogs, _ := ParseABCILogs(res.DeliverTx.Log) return &TxResponse{ Height: res.Height, TxHash: txHash, Codespace: res.DeliverTx.Codespace, Code: res.DeliverTx.Code, Data: strings.ToUpper(hex.EncodeToString(res.DeliverTx.Data)), RawLog: res.DeliverTx.Log, Logs: parsedLogs, Info: res.DeliverTx.Info, GasWanted: res.DeliverTx.GasWanted, GasUsed: res.DeliverTx.GasUsed, } } // NewResponseFormatBroadcastTx returns a TxResponse given a ResultBroadcastTx from tendermint func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) *TxResponse { if res == nil { return nil } parsedLogs, _ := ParseABCILogs(res.Log) return &TxResponse{ Code: res.Code, Codespace: res.Codespace, Data: res.Data.String(), RawLog: res.Log, Logs: parsedLogs, TxHash: res.Hash.String(), } } func (r TxResponse) String() string { var sb strings.Builder sb.WriteString("Response:\n") if r.Height > 0 { sb.WriteString(fmt.Sprintf(" Height: %d\n", r.Height)) } if r.TxHash != "" { sb.WriteString(fmt.Sprintf(" TxHash: %s\n", r.TxHash)) } if r.Code > 0 { sb.WriteString(fmt.Sprintf(" Code: %d\n", r.Code)) } if r.Data != "" { sb.WriteString(fmt.Sprintf(" Data: %s\n", r.Data)) } if r.RawLog != "" { sb.WriteString(fmt.Sprintf(" Raw Log: %s\n", r.RawLog)) } if r.Logs != nil { sb.WriteString(fmt.Sprintf(" Logs: %s\n", r.Logs)) } if r.Info != "" { sb.WriteString(fmt.Sprintf(" Info: %s\n", r.Info)) } if r.GasWanted != 0 { sb.WriteString(fmt.Sprintf(" GasWanted: %d\n", r.GasWanted)) } if r.GasUsed != 0 { sb.WriteString(fmt.Sprintf(" GasUsed: %d\n", r.GasUsed)) } if r.Codespace != "" { sb.WriteString(fmt.Sprintf(" Codespace: %s\n", r.Codespace)) } if r.Timestamp != "" { sb.WriteString(fmt.Sprintf(" Timestamp: %s\n", r.Timestamp)) } return strings.TrimSpace(sb.String()) } // Empty returns true if the response is empty func (r TxResponse) Empty() bool { return r.TxHash == "" && r.Logs == nil } func NewSearchTxsResult(totalCount, count, page, limit uint64, txs []*TxResponse) *SearchTxsResult { return &SearchTxsResult{ TotalCount: totalCount, Count: count, PageNumber: page, PageTotal: uint64(math.Ceil(float64(totalCount) / float64(limit))), Limit: limit, Txs: txs, } } // ParseABCILogs attempts to parse a stringified ABCI tx log into a slice of // ABCIMessageLog types. It returns an error upon JSON decoding failure. func ParseABCILogs(logs string) (res ABCIMessageLogs, err error) { err = json.Unmarshal([]byte(logs), &res) return res, err } var _, _ codectypes.UnpackInterfacesMessage = SearchTxsResult{}, TxResponse{} // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces // // types.UnpackInterfaces needs to be called for each nested Tx because // there are generally interfaces to unpack in Tx's func (s SearchTxsResult) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { for _, tx := range s.Txs { err := codectypes.UnpackInterfaces(tx, unpacker) if err != nil { return err } } return nil } // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (r TxResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { if r.Tx != nil { var tx Tx return unpacker.UnpackAny(r.Tx, &tx) } return nil } // GetTx unpacks the Tx from within a TxResponse and returns it func (r TxResponse) GetTx() Tx { if tx, ok := r.Tx.GetCachedValue().(Tx); ok { return tx } return nil } // WrapServiceResult wraps a result from a protobuf RPC service method call in // a Result object or error. This method takes care of marshaling the res param to // protobuf and attaching any events on the ctx.EventManager() to the Result. func WrapServiceResult(ctx Context, res proto.Message, err error) (*Result, error) { if err != nil { return nil, err } var data []byte if res != nil { data, err = proto.Marshal(res) if err != nil { return nil, err } } var events []abci.Event if evtMgr := ctx.EventManager(); evtMgr != nil { events = evtMgr.ABCIEvents() } return &Result{ Data: data, Events: events, }, nil }