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 }