package types import ( "bytes" "errors" "fmt" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" tmbytes "github.com/tendermint/tendermint/libs/bytes" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) // Tx is an arbitrary byte array. // NOTE: Tx has no types at this level, so when wire encoded it's just length-prefixed. // Might we want types here ? type Tx []byte // Hash computes the TMHASH hash of the wire encoded transaction. func (tx Tx) Hash() []byte { return tmhash.Sum(tx) } // String returns the hex-encoded transaction as a string. func (tx Tx) String() string { return fmt.Sprintf("Tx{%X}", []byte(tx)) } // Txs is a slice of Tx. type Txs []Tx // Hash returns the Merkle root hash of the transaction hashes. // i.e. the leaves of the tree are the hashes of the txs. func (txs Txs) Hash() []byte { // These allocations will be removed once Txs is switched to [][]byte, // ref #2603. This is because golang does not allow type casting slices without unsafe txBzs := make([][]byte, len(txs)) for i := 0; i < len(txs); i++ { txBzs[i] = txs[i].Hash() } return merkle.HashFromByteSlices(txBzs) } // Index returns the index of this transaction in the list, or -1 if not found func (txs Txs) Index(tx Tx) int { for i := range txs { if bytes.Equal(txs[i], tx) { return i } } return -1 } // IndexByHash returns the index of this transaction hash in the list, or -1 if not found func (txs Txs) IndexByHash(hash []byte) int { for i := range txs { if bytes.Equal(txs[i].Hash(), hash) { return i } } return -1 } // Proof returns a simple merkle proof for this node. // Panics if i < 0 or i >= len(txs) // TODO: optimize this! func (txs Txs) Proof(i int) TxProof { l := len(txs) bzs := make([][]byte, l) for i := 0; i < l; i++ { bzs[i] = txs[i].Hash() } root, proofs := merkle.ProofsFromByteSlices(bzs) return TxProof{ RootHash: root, Data: txs[i], Proof: *proofs[i], } } // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. type TxProof struct { RootHash tmbytes.HexBytes `json:"root_hash"` Data Tx `json:"data"` Proof merkle.Proof `json:"proof"` } // Leaf returns the hash(tx), which is the leaf in the merkle tree which this proof refers to. func (tp TxProof) Leaf() []byte { return tp.Data.Hash() } // Validate verifies the proof. It returns nil if the RootHash matches the dataHash argument, // and if the proof is internally consistent. Otherwise, it returns a sensible error. func (tp TxProof) Validate(dataHash []byte) error { if !bytes.Equal(dataHash, tp.RootHash) { return errors.New("proof matches different data hash") } if tp.Proof.Index < 0 { return errors.New("proof index cannot be negative") } if tp.Proof.Total <= 0 { return errors.New("proof total must be positive") } valid := tp.Proof.Verify(tp.RootHash, tp.Leaf()) if valid != nil { return errors.New("proof is not internally consistent") } return nil } func (tp TxProof) ToProto() tmproto.TxProof { pbProof := tp.Proof.ToProto() pbtp := tmproto.TxProof{ RootHash: tp.RootHash, Data: tp.Data, Proof: pbProof, } return pbtp } func TxProofFromProto(pb tmproto.TxProof) (TxProof, error) { pbProof, err := merkle.ProofFromProto(pb.Proof) if err != nil { return TxProof{}, err } pbtp := TxProof{ RootHash: pb.RootHash, Data: pb.Data, Proof: *pbProof, } return pbtp, nil } // ComputeProtoOverheadForTxs wraps the transactions in tmproto.Data{} and calculates the size. // https://developers.google.com/protocol-buffers/docs/encoding func ComputeProtoSizeForTxs(txs []Tx, tx Tx) int64 { tt := make(Txs, len(txs)+1) tt = append(tt, txs...) tt = append(tt, tx) data := Data{Txs: tt} pdData := data.ToProto() return int64(pdData.Size()) } // ComputeProtoSizeForTx wraps the transaction in tmproto.Data{} and calculates the size. // https://developers.google.com/protocol-buffers/docs/encoding func ComputeProtoSizeForTx(tx Tx) int64 { pbdata := tmproto.Data{Txs: [][]byte{tx}} return int64(pbdata.Size()) }