github.com/aakash4dev/cometbft@v0.38.2/types/tx.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "errors" 7 "fmt" 8 9 "github.com/aakash4dev/cometbft/crypto/merkle" 10 "github.com/aakash4dev/cometbft/crypto/tmhash" 11 cmtbytes "github.com/aakash4dev/cometbft/libs/bytes" 12 cmtproto "github.com/aakash4dev/cometbft/proto/tendermint/types" 13 ) 14 15 // TxKeySize is the size of the transaction key index 16 const TxKeySize = sha256.Size 17 18 type ( 19 // Tx is an arbitrary byte array. 20 // NOTE: Tx has no types at this level, so when wire encoded it's just length-prefixed. 21 // Might we want types here ? 22 Tx []byte 23 24 // TxKey is the fixed length array key used as an index. 25 TxKey [TxKeySize]byte 26 ) 27 28 // Hash computes the TMHASH hash of the wire encoded transaction. 29 func (tx Tx) Hash() []byte { 30 return tmhash.Sum(tx) 31 } 32 33 func (tx Tx) Key() TxKey { 34 return sha256.Sum256(tx) 35 } 36 37 // String returns the hex-encoded transaction as a string. 38 func (tx Tx) String() string { 39 return fmt.Sprintf("Tx{%X}", []byte(tx)) 40 } 41 42 // Txs is a slice of Tx. 43 type Txs []Tx 44 45 // Hash returns the Merkle root hash of the transaction hashes. 46 // i.e. the leaves of the tree are the hashes of the txs. 47 func (txs Txs) Hash() []byte { 48 hl := txs.hashList() 49 return merkle.HashFromByteSlices(hl) 50 } 51 52 // Index returns the index of this transaction in the list, or -1 if not found 53 func (txs Txs) Index(tx Tx) int { 54 for i := range txs { 55 if bytes.Equal(txs[i], tx) { 56 return i 57 } 58 } 59 return -1 60 } 61 62 // IndexByHash returns the index of this transaction hash in the list, or -1 if not found 63 func (txs Txs) IndexByHash(hash []byte) int { 64 for i := range txs { 65 if bytes.Equal(txs[i].Hash(), hash) { 66 return i 67 } 68 } 69 return -1 70 } 71 72 func (txs Txs) Proof(i int) TxProof { 73 hl := txs.hashList() 74 root, proofs := merkle.ProofsFromByteSlices(hl) 75 76 return TxProof{ 77 RootHash: root, 78 Data: txs[i], 79 Proof: *proofs[i], 80 } 81 } 82 83 func (txs Txs) hashList() [][]byte { 84 hl := make([][]byte, len(txs)) 85 for i := 0; i < len(txs); i++ { 86 hl[i] = txs[i].Hash() 87 } 88 return hl 89 } 90 91 // Txs is a slice of transactions. Sorting a Txs value orders the transactions 92 // lexicographically. 93 func (txs Txs) Len() int { return len(txs) } 94 func (txs Txs) Swap(i, j int) { txs[i], txs[j] = txs[j], txs[i] } 95 func (txs Txs) Less(i, j int) bool { 96 return bytes.Compare(txs[i], txs[j]) == -1 97 } 98 99 func ToTxs(txl [][]byte) Txs { 100 txs := make([]Tx, 0, len(txl)) 101 for _, tx := range txl { 102 txs = append(txs, tx) 103 } 104 return txs 105 } 106 107 func (txs Txs) Validate(maxSizeBytes int64) error { 108 var size int64 109 for _, tx := range txs { 110 size += int64(len(tx)) 111 if size > maxSizeBytes { 112 return fmt.Errorf("transaction data size exceeds maximum %d", maxSizeBytes) 113 } 114 } 115 return nil 116 } 117 118 // ToSliceOfBytes converts a Txs to slice of byte slices. 119 func (txs Txs) ToSliceOfBytes() [][]byte { 120 txBzs := make([][]byte, len(txs)) 121 for i := 0; i < len(txs); i++ { 122 txBzs[i] = txs[i] 123 } 124 return txBzs 125 } 126 127 // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. 128 type TxProof struct { 129 RootHash cmtbytes.HexBytes `json:"root_hash"` 130 Data Tx `json:"data"` 131 Proof merkle.Proof `json:"proof"` 132 } 133 134 // Leaf returns the hash(tx), which is the leaf in the merkle tree which this proof refers to. 135 func (tp TxProof) Leaf() []byte { 136 return tp.Data.Hash() 137 } 138 139 // Validate verifies the proof. It returns nil if the RootHash matches the dataHash argument, 140 // and if the proof is internally consistent. Otherwise, it returns a sensible error. 141 func (tp TxProof) Validate(dataHash []byte) error { 142 if !bytes.Equal(dataHash, tp.RootHash) { 143 return errors.New("proof matches different data hash") 144 } 145 if tp.Proof.Index < 0 { 146 return errors.New("proof index cannot be negative") 147 } 148 if tp.Proof.Total <= 0 { 149 return errors.New("proof total must be positive") 150 } 151 valid := tp.Proof.Verify(tp.RootHash, tp.Leaf()) 152 if valid != nil { 153 return errors.New("proof is not internally consistent") 154 } 155 return nil 156 } 157 158 func (tp TxProof) ToProto() cmtproto.TxProof { 159 160 pbProof := tp.Proof.ToProto() 161 162 pbtp := cmtproto.TxProof{ 163 RootHash: tp.RootHash, 164 Data: tp.Data, 165 Proof: pbProof, 166 } 167 168 return pbtp 169 } 170 func TxProofFromProto(pb cmtproto.TxProof) (TxProof, error) { 171 172 pbProof, err := merkle.ProofFromProto(pb.Proof) 173 if err != nil { 174 return TxProof{}, err 175 } 176 177 pbtp := TxProof{ 178 RootHash: pb.RootHash, 179 Data: pb.Data, 180 Proof: *pbProof, 181 } 182 183 return pbtp, nil 184 } 185 186 // ComputeProtoSizeForTxs wraps the transactions in cmtproto.Data{} and calculates the size. 187 // https://developers.google.com/protocol-buffers/docs/encoding 188 func ComputeProtoSizeForTxs(txs []Tx) int64 { 189 data := Data{Txs: txs} 190 pdData := data.ToProto() 191 return int64(pdData.Size()) 192 }