code.vegaprotocol.io/vega@v0.79.0/blockexplorer/entities/transaction.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package entities
    17  
    18  import (
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/commands"
    25  	"code.vegaprotocol.io/vega/libs/ptr"
    26  	pb "code.vegaprotocol.io/vega/protos/blockexplorer/api/v1"
    27  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    28  
    29  	tmTypes "github.com/cometbft/cometbft/abci/types"
    30  	"google.golang.org/protobuf/proto"
    31  )
    32  
    33  type TxResultRow struct {
    34  	RowID       int64     `db:"rowid"`
    35  	BlockHeight int64     `db:"block_height"`
    36  	Index       int64     `db:"index"`
    37  	CreatedAt   time.Time `db:"created_at"`
    38  	TxHash      string    `db:"tx_hash"`
    39  	TxResult    []byte    `db:"tx_result"`
    40  	Submitter   string    `db:"submitter"`
    41  	CmdType     string    `db:"cmd_type"`
    42  }
    43  
    44  func (t *TxResultRow) ToProto() (*pb.Transaction, error) {
    45  	txResult := tmTypes.TxResult{}
    46  	if err := txResult.Unmarshal(t.TxResult); err != nil {
    47  		return nil, fmt.Errorf("unmarshalling tendermint tx result: %w", err)
    48  	}
    49  
    50  	cTx := commandspb.Transaction{}
    51  	if err := proto.Unmarshal(txResult.Tx, &cTx); err != nil {
    52  		return nil, fmt.Errorf("unmarshalling vega transaction: %w", err)
    53  	}
    54  
    55  	inputData, err := commands.UnmarshalInputData(cTx.InputData)
    56  	if err != nil {
    57  		return nil, fmt.Errorf("unmarshalling vega input data: %w", err)
    58  	}
    59  
    60  	cursor := t.Cursor()
    61  
    62  	var strErr *string
    63  	if txResult.Result.Code != 0 {
    64  		strErr = ptr.From(string(txResult.Result.Data))
    65  	}
    66  
    67  	return &pb.Transaction{
    68  		Block:     uint64(t.BlockHeight),
    69  		Index:     uint32(t.Index),
    70  		Type:      extractAttribute(&txResult, "command", "type"),
    71  		Submitter: extractAttribute(&txResult, "tx", "submitter"),
    72  		Code:      txResult.Result.Code,
    73  		Error:     strErr,
    74  		Hash:      t.TxHash,
    75  		Cursor:    cursor.String(),
    76  		Command:   inputData,
    77  		Signature: cTx.Signature,
    78  		Pow:       cTx.Pow,
    79  		Version:   cTx.Version,
    80  		CreatedAt: t.CreatedAt.Format(time.RFC3339),
    81  	}, nil
    82  }
    83  
    84  func (t *TxResultRow) Cursor() TxCursor {
    85  	return TxCursor{
    86  		BlockNumber: uint64(t.BlockHeight),
    87  		TxIndex:     uint32(t.Index),
    88  	}
    89  }
    90  
    91  func extractAttribute(r *tmTypes.TxResult, eType, key string) string {
    92  	for _, e := range r.Result.Events {
    93  		if e.Type == eType {
    94  			for _, a := range e.Attributes {
    95  				if a.Key == key {
    96  					return a.Value
    97  				}
    98  			}
    99  		}
   100  	}
   101  	return ""
   102  }
   103  
   104  type TxCursor struct {
   105  	BlockNumber uint64
   106  	TxIndex     uint32
   107  }
   108  
   109  func TxCursorFromString(s string) (TxCursor, error) {
   110  	parts := strings.Split(s, ".")
   111  	if len(parts) != 2 {
   112  		return TxCursor{}, fmt.Errorf("invalid cursor string: %s", s)
   113  	}
   114  
   115  	blockNumber, err := strconv.ParseUint(parts[0], 10, 64)
   116  	if err != nil {
   117  		return TxCursor{}, fmt.Errorf("invalid block number: %w", err)
   118  	}
   119  
   120  	txIndex, err := strconv.ParseUint(parts[1], 10, 32)
   121  	if err != nil {
   122  		return TxCursor{}, fmt.Errorf("invalid transaction index: %w", err)
   123  	}
   124  
   125  	return TxCursor{
   126  		BlockNumber: blockNumber,
   127  		TxIndex:     uint32(txIndex),
   128  	}, nil
   129  }
   130  
   131  func (c *TxCursor) String() string {
   132  	return fmt.Sprintf("%d.%d", c.BlockNumber, c.TxIndex)
   133  }
   134  
   135  // AreValidCursorBoundaries checks if the start and end cursors creates valid
   136  // set boundaries for cursors, as: [start, end].
   137  func AreValidCursorBoundaries(start, end *TxCursor) bool {
   138  	if start.BlockNumber == end.BlockNumber {
   139  		return start.TxIndex < end.TxIndex
   140  	}
   141  
   142  	return start.BlockNumber < end.BlockNumber
   143  }