github.com/Finschia/ostracon@v1.1.5/types/tx.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"errors"
     7  	"fmt"
     8  
     9  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    10  
    11  	"github.com/Finschia/ostracon/crypto/merkle"
    12  	"github.com/Finschia/ostracon/crypto/tmhash"
    13  	tmbytes "github.com/Finschia/ostracon/libs/bytes"
    14  )
    15  
    16  // TxKeySize is the size of the transaction key index
    17  const TxKeySize = sha256.Size
    18  
    19  type (
    20  	// Tx is an arbitrary byte array.
    21  	// NOTE: Tx has no types at this level, so when wire encoded it's just length-prefixed.
    22  	// Might we want types here ?
    23  	Tx []byte
    24  
    25  	// TxKey is the fixed length array key used as an index.
    26  	TxKey [TxKeySize]byte
    27  )
    28  
    29  // Hash computes the OCHASH hash of the wire encoded transaction.
    30  func (tx Tx) Hash() []byte {
    31  	return tmhash.Sum(tx)
    32  }
    33  
    34  func (tx Tx) Key() TxKey {
    35  	return sha256.Sum256(tx)
    36  }
    37  
    38  // String returns the hex-encoded transaction as a string.
    39  func (tx Tx) String() string {
    40  	return fmt.Sprintf("Tx{%X}", []byte(tx))
    41  }
    42  
    43  // Txs is a slice of Tx.
    44  type Txs []Tx
    45  
    46  // Hash returns the Merkle root hash of the transaction hashes.
    47  // i.e. the leaves of the tree are the hashes of the txs.
    48  func (txs Txs) Hash() []byte {
    49  	// These allocations will be removed once Txs is switched to [][]byte,
    50  	// ref #2603. This is because golang does not allow type casting slices without unsafe
    51  	txBzs := make([][]byte, len(txs))
    52  	for i := 0; i < len(txs); i++ {
    53  		txBzs[i] = txs[i].Hash()
    54  	}
    55  	return merkle.HashFromByteSlices(txBzs)
    56  }
    57  
    58  // Index returns the index of this transaction in the list, or -1 if not found
    59  func (txs Txs) Index(tx Tx) int {
    60  	for i := range txs {
    61  		if bytes.Equal(txs[i], tx) {
    62  			return i
    63  		}
    64  	}
    65  	return -1
    66  }
    67  
    68  // IndexByHash returns the index of this transaction hash in the list, or -1 if not found
    69  func (txs Txs) IndexByHash(hash []byte) int {
    70  	for i := range txs {
    71  		if bytes.Equal(txs[i].Hash(), hash) {
    72  			return i
    73  		}
    74  	}
    75  	return -1
    76  }
    77  
    78  // Proof returns a simple merkle proof for this node.
    79  // Panics if i < 0 or i >= len(txs)
    80  // TODO: optimize this!
    81  func (txs Txs) Proof(i int) TxProof {
    82  	l := len(txs)
    83  	bzs := make([][]byte, l)
    84  	for i := 0; i < l; i++ {
    85  		bzs[i] = txs[i].Hash()
    86  	}
    87  	root, proofs := merkle.ProofsFromByteSlices(bzs)
    88  
    89  	return TxProof{
    90  		RootHash: root,
    91  		Data:     txs[i],
    92  		Proof:    *proofs[i],
    93  	}
    94  }
    95  
    96  // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree.
    97  type TxProof struct {
    98  	RootHash tmbytes.HexBytes `json:"root_hash"`
    99  	Data     Tx               `json:"data"`
   100  	Proof    merkle.Proof     `json:"proof"`
   101  }
   102  
   103  // Leaf returns the hash(tx), which is the leaf in the merkle tree which this proof refers to.
   104  func (tp TxProof) Leaf() []byte {
   105  	return tp.Data.Hash()
   106  }
   107  
   108  // Validate verifies the proof. It returns nil if the RootHash matches the dataHash argument,
   109  // and if the proof is internally consistent. Otherwise, it returns a sensible error.
   110  func (tp TxProof) Validate(dataHash []byte) error {
   111  	if !bytes.Equal(dataHash, tp.RootHash) {
   112  		return errors.New("proof matches different data hash")
   113  	}
   114  	if tp.Proof.Index < 0 {
   115  		return errors.New("proof index cannot be negative")
   116  	}
   117  	if tp.Proof.Total <= 0 {
   118  		return errors.New("proof total must be positive")
   119  	}
   120  	valid := tp.Proof.Verify(tp.RootHash, tp.Leaf())
   121  	if valid != nil {
   122  		return errors.New("proof is not internally consistent")
   123  	}
   124  	return nil
   125  }
   126  
   127  func (tp TxProof) ToProto() tmproto.TxProof {
   128  
   129  	pbProof := tp.Proof.ToProto()
   130  
   131  	pbtp := tmproto.TxProof{
   132  		RootHash: tp.RootHash,
   133  		Data:     tp.Data,
   134  		Proof:    pbProof,
   135  	}
   136  
   137  	return pbtp
   138  }
   139  func TxProofFromProto(pb tmproto.TxProof) (TxProof, error) {
   140  
   141  	pbProof, err := merkle.ProofFromProto(pb.Proof)
   142  	if err != nil {
   143  		return TxProof{}, err
   144  	}
   145  
   146  	pbtp := TxProof{
   147  		RootHash: pb.RootHash,
   148  		Data:     pb.Data,
   149  		Proof:    *pbProof,
   150  	}
   151  
   152  	return pbtp, nil
   153  }
   154  
   155  // ComputeProtoSizeForTxs wraps the transactions in tmproto.Data{} and calculates the size.
   156  // https://developers.google.com/protocol-buffers/docs/encoding
   157  func ComputeProtoSizeForTxs(txs []Tx) int64 {
   158  	data := Data{Txs: txs}
   159  	pdData := data.ToProto()
   160  	return int64(pdData.Size())
   161  }