You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.4 KiB

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