github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/types/tx.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/gnolang/gno/tm2/pkg/amino"
     9  	abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
    10  	"github.com/gnolang/gno/tm2/pkg/crypto/merkle"
    11  	"github.com/gnolang/gno/tm2/pkg/crypto/tmhash"
    12  )
    13  
    14  // Tx is an arbitrary byte array.
    15  // NOTE: Tx has no types at this level, so when wire encoded it's just length-prefixed.
    16  // Might we want types here ?
    17  type Tx []byte
    18  
    19  // Hash computes the TMHASH hash of the wire encoded transaction.
    20  func (tx Tx) Hash() []byte {
    21  	return tmhash.Sum(tx)
    22  }
    23  
    24  // String returns the hex-encoded transaction as a string.
    25  func (tx Tx) String() string {
    26  	return fmt.Sprintf("Tx{%X}", []byte(tx))
    27  }
    28  
    29  // Txs is a slice of Tx.
    30  type Txs []Tx
    31  
    32  // Hash returns the Merkle root hash of the transaction hashes.
    33  // i.e. the leaves of the tree are the hashes of the txs.
    34  func (txs Txs) Hash() []byte {
    35  	// These allocations will be removed once Txs is switched to [][]byte,
    36  	// ref #2603. This is because golang does not allow type casting slices without unsafe
    37  	txBzs := make([][]byte, len(txs))
    38  	for i := 0; i < len(txs); i++ {
    39  		txBzs[i] = txs[i].Hash()
    40  	}
    41  	return merkle.SimpleHashFromByteSlices(txBzs)
    42  }
    43  
    44  // Index returns the index of this transaction in the list, or -1 if not found
    45  func (txs Txs) Index(tx Tx) int {
    46  	for i := range txs {
    47  		if bytes.Equal(txs[i], tx) {
    48  			return i
    49  		}
    50  	}
    51  	return -1
    52  }
    53  
    54  // IndexByHash returns the index of this transaction hash in the list, or -1 if not found
    55  func (txs Txs) IndexByHash(hash []byte) int {
    56  	for i := range txs {
    57  		if bytes.Equal(txs[i].Hash(), hash) {
    58  			return i
    59  		}
    60  	}
    61  	return -1
    62  }
    63  
    64  // Proof returns a simple merkle proof for this node.
    65  // Panics if i < 0 or i >= len(txs)
    66  // TODO: optimize this!
    67  func (txs Txs) Proof(i int) TxProof {
    68  	l := len(txs)
    69  	bzs := make([][]byte, l)
    70  	for i := 0; i < l; i++ {
    71  		bzs[i] = txs[i].Hash()
    72  	}
    73  	root, proofs := merkle.SimpleProofsFromByteSlices(bzs)
    74  
    75  	return TxProof{
    76  		RootHash: root,
    77  		Data:     txs[i],
    78  		Proof:    *proofs[i],
    79  	}
    80  }
    81  
    82  // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree.
    83  type TxProof struct {
    84  	RootHash []byte             `json:"root_hash"`
    85  	Data     Tx                 `json:"data"`
    86  	Proof    merkle.SimpleProof `json:"proof"`
    87  }
    88  
    89  // Leaf returns the hash(tx), which is the leaf in the merkle tree which this proof refers to.
    90  func (tp TxProof) Leaf() []byte {
    91  	return tp.Data.Hash()
    92  }
    93  
    94  // Validate verifies the proof. It returns nil if the RootHash matches the dataHash argument,
    95  // and if the proof is internally consistent. Otherwise, it returns a sensible error.
    96  func (tp TxProof) Validate(dataHash []byte) error {
    97  	if !bytes.Equal(dataHash, tp.RootHash) {
    98  		return errors.New("Proof matches different data hash")
    99  	}
   100  	if tp.Proof.Index < 0 {
   101  		return errors.New("Proof index cannot be negative")
   102  	}
   103  	if tp.Proof.Total <= 0 {
   104  		return errors.New("Proof total must be positive")
   105  	}
   106  	valid := tp.Proof.Verify(tp.RootHash, tp.Leaf())
   107  	if valid != nil {
   108  		return errors.New("Proof is not internally consistent")
   109  	}
   110  	return nil
   111  }
   112  
   113  // TxResult contains results of executing the transaction.
   114  type TxResult struct {
   115  	Height   int64                  `json:"height"`
   116  	Index    uint32                 `json:"index"`
   117  	Tx       Tx                     `json:"tx"`
   118  	Response abci.ResponseDeliverTx `json:"response"`
   119  }
   120  
   121  func (tx *TxResult) Bytes() []byte {
   122  	return amino.MustMarshal(tx)
   123  }