github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/result.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"fmt"
     8  	"math"
     9  	"strings"
    10  
    11  	"github.com/gogo/protobuf/proto"
    12  
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    14  	"github.com/tendermint/go-amino"
    15  
    16  	ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    17  )
    18  
    19  // GasInfo defines tx execution gas context.
    20  type GasInfo struct {
    21  	// GasWanted is the maximum units of work we allow this tx to perform.
    22  	GasWanted uint64
    23  
    24  	// GasUsed is the amount of gas actually consumed.
    25  	GasUsed uint64
    26  }
    27  
    28  // Result is the union of ResponseFormat and ResponseCheckTx.
    29  type Result struct {
    30  	// Data is any data returned from message or handler execution. It MUST be length
    31  	// prefixed in order to separate data from multiple message executions.
    32  	Data []byte
    33  
    34  	// Log contains the log information from message or handler execution.
    35  	Log string
    36  
    37  	// Events contains a slice of Event objects that were emitted during message or
    38  	// handler execution.
    39  	Events Events
    40  }
    41  
    42  // SimulationResponse defines the response generated when a transaction is successfully
    43  // simulated by the Baseapp.
    44  type SimulationResponse struct {
    45  	GasInfo
    46  	Result *Result
    47  }
    48  
    49  // ABCIMessageLogs represents a slice of ABCIMessageLog.
    50  type ABCIMessageLogs []ABCIMessageLog
    51  
    52  // ABCIMessageLog defines a structure containing an indexed tx ABCI message log.
    53  type ABCIMessageLog struct {
    54  	MsgIndex uint16 `json:"msg_index"`
    55  	Log      string `json:"log"`
    56  
    57  	// Events contains a slice of Event objects that were emitted during some
    58  	// execution.
    59  	Events StringEvents `json:"events"`
    60  }
    61  
    62  func (log ABCIMessageLog) MarshalJsonToBuffer(buf *bytes.Buffer) error {
    63  	var err error
    64  
    65  	err = buf.WriteByte('{')
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	buf.WriteString(`"msg_index":`)
    71  	blob, err := json.Marshal(log.MsgIndex)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	_, err = buf.Write(blob)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	err = buf.WriteByte(',')
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	buf.WriteString(`"log":`)
    85  	blob, err = json.Marshal(log.Log)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	_, err = buf.Write(blob)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	err = buf.WriteByte(',')
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	buf.WriteString(`"events":`)
    99  	err = log.Events.MarshalJsonToBuffer(buf)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	return buf.WriteByte('}')
   105  }
   106  
   107  func NewABCIMessageLog(i uint16, log string, events Events) ABCIMessageLog {
   108  	return ABCIMessageLog{
   109  		MsgIndex: i,
   110  		Log:      log,
   111  		Events:   StringifyEvents(events),
   112  	}
   113  }
   114  
   115  // String implements the fmt.Stringer interface for the ABCIMessageLogs type.
   116  func (logs ABCIMessageLogs) String() (str string) {
   117  	if logs != nil {
   118  		raw, err := logs.MarshalToJson()
   119  		if err != nil {
   120  			raw, err = codec.Cdc.MarshalJSON(logs)
   121  		}
   122  		if err == nil {
   123  			str = string(raw)
   124  		}
   125  	}
   126  
   127  	return str
   128  }
   129  
   130  var abciMessageLogsBufferPool = amino.NewBufferPool()
   131  
   132  func (logs ABCIMessageLogs) MarshalToJson() ([]byte, error) {
   133  	buf := abciMessageLogsBufferPool.Get()
   134  	defer abciMessageLogsBufferPool.Put(buf)
   135  	err := logs.MarshalJsonToBuffer(buf)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	return amino.GetBytesBufferCopy(buf), nil
   140  }
   141  
   142  func (logs ABCIMessageLogs) MarshalJsonToBuffer(buf *bytes.Buffer) error {
   143  	var err error
   144  	if logs == nil {
   145  		_, err = buf.WriteString("null")
   146  		return err
   147  	}
   148  
   149  	err = buf.WriteByte('[')
   150  	if err != nil {
   151  		return err
   152  	}
   153  	for i, log := range logs {
   154  		if i != 0 {
   155  			err = buf.WriteByte(',')
   156  			if err != nil {
   157  				return err
   158  			}
   159  		}
   160  		err = log.MarshalJsonToBuffer(buf)
   161  		if err != nil {
   162  			return err
   163  		}
   164  	}
   165  	return buf.WriteByte(']')
   166  }
   167  
   168  // TxResponse defines a structure containing relevant tx data and metadata. The
   169  // tags are stringified and the log is JSON decoded.
   170  type TxResponse struct {
   171  	Height    int64           `json:"height"`
   172  	TxHash    string          `json:"txhash"`
   173  	Codespace string          `json:"codespace,omitempty"`
   174  	Code      uint32          `json:"code,omitempty"`
   175  	Data      string          `json:"data,omitempty"`
   176  	RawLog    string          `json:"raw_log,omitempty"`
   177  	Logs      ABCIMessageLogs `json:"logs,omitempty"`
   178  	Info      string          `json:"info,omitempty"`
   179  	GasWanted int64           `json:"gas_wanted,omitempty"`
   180  	GasUsed   int64           `json:"gas_used,omitempty"`
   181  	Tx        Tx              `json:"tx,omitempty"`
   182  	Timestamp string          `json:"timestamp,omitempty"`
   183  }
   184  
   185  // NewResponseResultTx returns a TxResponse given a ResultTx from tendermint
   186  func NewResponseResultTx(res *ctypes.ResultTx, tx Tx, timestamp string) TxResponse {
   187  	if res == nil {
   188  		return TxResponse{}
   189  	}
   190  
   191  	parsedLogs, _ := ParseABCILogs(res.TxResult.Log)
   192  
   193  	return TxResponse{
   194  		TxHash:    res.Hash.String(),
   195  		Height:    res.Height,
   196  		Codespace: res.TxResult.Codespace,
   197  		Code:      res.TxResult.Code,
   198  		Data:      strings.ToUpper(hex.EncodeToString(res.TxResult.Data)),
   199  		RawLog:    res.TxResult.Log,
   200  		Logs:      parsedLogs,
   201  		Info:      res.TxResult.Info,
   202  		GasWanted: res.TxResult.GasWanted,
   203  		GasUsed:   res.TxResult.GasUsed,
   204  		Tx:        tx,
   205  		Timestamp: timestamp,
   206  	}
   207  }
   208  
   209  // NewResponseFormatBroadcastTxCommit returns a TxResponse given a
   210  // ResultBroadcastTxCommit from tendermint.
   211  func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) TxResponse {
   212  	if res == nil {
   213  		return TxResponse{}
   214  	}
   215  
   216  	if !res.CheckTx.IsOK() {
   217  		return newTxResponseCheckTx(res)
   218  	}
   219  
   220  	return newTxResponseDeliverTx(res)
   221  }
   222  
   223  func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) TxResponse {
   224  	if res == nil {
   225  		return TxResponse{}
   226  	}
   227  
   228  	var txHash string
   229  	if res.Hash != nil {
   230  		txHash = res.Hash.String()
   231  	}
   232  
   233  	parsedLogs, _ := ParseABCILogs(res.CheckTx.Log)
   234  
   235  	return TxResponse{
   236  		Height:    res.Height,
   237  		TxHash:    txHash,
   238  		Codespace: res.CheckTx.Codespace,
   239  		Code:      res.CheckTx.Code,
   240  		Data:      strings.ToUpper(hex.EncodeToString(res.CheckTx.Data)),
   241  		RawLog:    res.CheckTx.Log,
   242  		Logs:      parsedLogs,
   243  		Info:      res.CheckTx.Info,
   244  		GasWanted: res.CheckTx.GasWanted,
   245  		GasUsed:   res.CheckTx.GasUsed,
   246  	}
   247  }
   248  
   249  func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) TxResponse {
   250  	if res == nil {
   251  		return TxResponse{}
   252  	}
   253  
   254  	var txHash string
   255  	if res.Hash != nil {
   256  		txHash = res.Hash.String()
   257  	}
   258  
   259  	parsedLogs, _ := ParseABCILogs(res.DeliverTx.Log)
   260  
   261  	return TxResponse{
   262  		Height:    res.Height,
   263  		TxHash:    txHash,
   264  		Codespace: res.DeliverTx.Codespace,
   265  		Code:      res.DeliverTx.Code,
   266  		Data:      strings.ToUpper(hex.EncodeToString(res.DeliverTx.Data)),
   267  		RawLog:    res.DeliverTx.Log,
   268  		Logs:      parsedLogs,
   269  		Info:      res.DeliverTx.Info,
   270  		GasWanted: res.DeliverTx.GasWanted,
   271  		GasUsed:   res.DeliverTx.GasUsed,
   272  	}
   273  }
   274  
   275  // NewResponseFormatBroadcastTx returns a TxResponse given a ResultBroadcastTx from tendermint
   276  func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) TxResponse {
   277  	if res == nil {
   278  		return TxResponse{}
   279  	}
   280  
   281  	parsedLogs, _ := ParseABCILogs(res.Log)
   282  
   283  	return TxResponse{
   284  		Code:   res.Code,
   285  		Data:   res.Data.String(),
   286  		RawLog: res.Log,
   287  		Logs:   parsedLogs,
   288  		TxHash: res.Hash.String(),
   289  	}
   290  }
   291  
   292  func (r TxResponse) String() string {
   293  	var sb strings.Builder
   294  	sb.WriteString("Response:\n")
   295  
   296  	if r.Height > 0 {
   297  		sb.WriteString(fmt.Sprintf("  Height: %d\n", r.Height))
   298  	}
   299  	if r.TxHash != "" {
   300  		sb.WriteString(fmt.Sprintf("  TxHash: %s\n", r.TxHash))
   301  	}
   302  	if r.Code > 0 {
   303  		sb.WriteString(fmt.Sprintf("  Code: %d\n", r.Code))
   304  	}
   305  	if r.Data != "" {
   306  		sb.WriteString(fmt.Sprintf("  Data: %s\n", r.Data))
   307  	}
   308  	if r.RawLog != "" {
   309  		sb.WriteString(fmt.Sprintf("  Raw Log: %s\n", r.RawLog))
   310  	}
   311  	if r.Logs != nil {
   312  		sb.WriteString(fmt.Sprintf("  Logs: %s\n", r.Logs))
   313  	}
   314  	if r.Info != "" {
   315  		sb.WriteString(fmt.Sprintf("  Info: %s\n", r.Info))
   316  	}
   317  	if r.GasWanted != 0 {
   318  		sb.WriteString(fmt.Sprintf("  GasWanted: %d\n", r.GasWanted))
   319  	}
   320  	if r.GasUsed != 0 {
   321  		sb.WriteString(fmt.Sprintf("  GasUsed: %d\n", r.GasUsed))
   322  	}
   323  	if r.Codespace != "" {
   324  		sb.WriteString(fmt.Sprintf("  Codespace: %s\n", r.Codespace))
   325  	}
   326  	if r.Timestamp != "" {
   327  		sb.WriteString(fmt.Sprintf("  Timestamp: %s\n", r.Timestamp))
   328  	}
   329  
   330  	return strings.TrimSpace(sb.String())
   331  }
   332  
   333  // Empty returns true if the response is empty
   334  func (r TxResponse) Empty() bool {
   335  	return r.TxHash == "" && r.Logs == nil
   336  }
   337  
   338  // SearchTxsResult defines a structure for querying txs pageable
   339  type SearchTxsResult struct {
   340  	TotalCount int          `json:"total_count"` // Count of all txs
   341  	Count      int          `json:"count"`       // Count of txs in current page
   342  	PageNumber int          `json:"page_number"` // Index of current page, start from 1
   343  	PageTotal  int          `json:"page_total"`  // Count of total pages
   344  	Limit      int          `json:"limit"`       // Max count txs per page
   345  	Txs        []TxResponse `json:"txs"`         // List of txs in current page
   346  }
   347  
   348  func NewSearchTxsResult(totalCount, count, page, limit int, txs []TxResponse) SearchTxsResult {
   349  	return SearchTxsResult{
   350  		TotalCount: totalCount,
   351  		Count:      count,
   352  		PageNumber: page,
   353  		PageTotal:  int(math.Ceil(float64(totalCount) / float64(limit))),
   354  		Limit:      limit,
   355  		Txs:        txs,
   356  	}
   357  }
   358  
   359  // ParseABCILogs attempts to parse a stringified ABCI tx log into a slice of
   360  // ABCIMessageLog types. It returns an error upon JSON decoding failure.
   361  func ParseABCILogs(logs string) (res ABCIMessageLogs, err error) {
   362  	err = json.Unmarshal([]byte(logs), &res)
   363  	return res, err
   364  }
   365  
   366  // WrapServiceResult wraps a result from a protobuf RPC service method call in
   367  // a Result object or error. This method takes care of marshaling the res param to
   368  // protobuf and attaching any events on the ctx.EventManager() to the Result.
   369  func WrapServiceResult(ctx Context, res proto.Message, err error) (*Result, error) {
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  
   374  	var data []byte
   375  	if res != nil {
   376  		data, err = proto.Marshal(res)
   377  		if err != nil {
   378  			return nil, err
   379  		}
   380  	}
   381  
   382  	var events []Event
   383  	if evtMgr := ctx.EventManager(); evtMgr != nil {
   384  		events = evtMgr.Events()
   385  	}
   386  
   387  	return &Result{
   388  		Data:   data,
   389  		Events: events,
   390  	}, nil
   391  }