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 }