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  }