github.com/project-88388/tendermint-v0.34.14-terra.2@v1.0.0/types/tx.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "github.com/tendermint/tendermint/crypto/merkle" 9 "github.com/tendermint/tendermint/crypto/tmhash" 10 tmbytes "github.com/tendermint/tendermint/libs/bytes" 11 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 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.HashFromByteSlices(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.ProofsFromByteSlices(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 tmbytes.HexBytes `json:"root_hash"` 85 Data Tx `json:"data"` 86 Proof merkle.Proof `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 func (tp TxProof) ToProto() tmproto.TxProof { 114 115 pbProof := tp.Proof.ToProto() 116 117 pbtp := tmproto.TxProof{ 118 RootHash: tp.RootHash, 119 Data: tp.Data, 120 Proof: pbProof, 121 } 122 123 return pbtp 124 } 125 func TxProofFromProto(pb tmproto.TxProof) (TxProof, error) { 126 127 pbProof, err := merkle.ProofFromProto(pb.Proof) 128 if err != nil { 129 return TxProof{}, err 130 } 131 132 pbtp := TxProof{ 133 RootHash: pb.RootHash, 134 Data: pb.Data, 135 Proof: *pbProof, 136 } 137 138 return pbtp, nil 139 } 140 141 // ComputeProtoSizeForTxs wraps the transactions in tmproto.Data{} and calculates the size. 142 // https://developers.google.com/protocol-buffers/docs/encoding 143 func ComputeProtoSizeForTxs(txs []Tx) int64 { 144 data := Data{Txs: txs} 145 pdData := data.ToProto() 146 return int64(pdData.Size()) 147 }