store.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package shachain
  2. import (
  3. "encoding/binary"
  4. "io"
  5. "github.com/btcsuite/btcd/chaincfg/chainhash"
  6. "github.com/go-errors/errors"
  7. )
  8. // Store is an interface which serves as an abstraction over data structure
  9. // responsible for efficiently storing and restoring of hash secrets by given
  10. // indexes.
  11. //
  12. // Description: The Lightning Network wants a chain of (say 1 million)
  13. // unguessable 256 bit values; we generate them and send them one at a time to
  14. // a remote node. We don't want the remote node to have to store all the
  15. // values, so it's better if they can derive them once they see them.
  16. type Store interface {
  17. // LookUp function is used to restore/lookup/fetch the previous secret
  18. // by its index.
  19. LookUp(uint64) (*chainhash.Hash, error)
  20. // AddNextEntry attempts to store the given hash within its internal
  21. // storage in an efficient manner.
  22. //
  23. // NOTE: The hashes derived from the shachain MUST be inserted in the
  24. // order they're produced by a shachain.Producer.
  25. AddNextEntry(*chainhash.Hash) error
  26. // Encode writes a binary serialization of the shachain elements
  27. // currently saved by implementation of shachain.Store to the passed
  28. // io.Writer.
  29. Encode(io.Writer) error
  30. }
  31. // RevocationStore is a concrete implementation of the Store interface. The
  32. // revocation store is able to efficiently store N derived shachain elements in
  33. // a space efficient manner with a space complexity of O(log N). The original
  34. // description of the storage methodology can be found here:
  35. // https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#efficient-per-commitment-secret-storage
  36. type RevocationStore struct {
  37. // lenBuckets stores the number of currently active buckets.
  38. lenBuckets uint8
  39. // buckets is an array of elements from which we may derive all
  40. // previous elements, each bucket corresponds to the element with the
  41. // particular number of trailing zeros.
  42. buckets [maxHeight]element
  43. // index is an available index which will be assigned to the new
  44. // element.
  45. index index
  46. }
  47. // A compile time check to ensure RevocationStore implements the Store
  48. // interface.
  49. var _ Store = (*RevocationStore)(nil)
  50. // NewRevocationStore creates the new shachain store.
  51. func NewRevocationStore() *RevocationStore {
  52. return &RevocationStore{
  53. lenBuckets: 0,
  54. index: startIndex,
  55. }
  56. }
  57. // NewRevocationStoreFromBytes recreates the initial store state from the given
  58. // binary shachain store representation.
  59. func NewRevocationStoreFromBytes(r io.Reader) (*RevocationStore, error) {
  60. store := &RevocationStore{}
  61. if err := binary.Read(r, binary.BigEndian, &store.lenBuckets); err != nil {
  62. return nil, err
  63. }
  64. for i := uint8(0); i < store.lenBuckets; i++ {
  65. var hashIndex index
  66. err := binary.Read(r, binary.BigEndian, &hashIndex)
  67. if err != nil {
  68. return nil, err
  69. }
  70. var nextHash chainhash.Hash
  71. if _, err := io.ReadFull(r, nextHash[:]); err != nil {
  72. return nil, err
  73. }
  74. store.buckets[i] = element{
  75. index: hashIndex,
  76. hash: nextHash,
  77. }
  78. }
  79. if err := binary.Read(r, binary.BigEndian, &store.index); err != nil {
  80. return nil, err
  81. }
  82. return store, nil
  83. }
  84. // LookUp function is used to restore/lookup/fetch the previous secret by its
  85. // index. If secret which corresponds to given index was not previously placed
  86. // in store we will not able to derive it and function will fail.
  87. //
  88. // NOTE: This function is part of the Store interface.
  89. func (store *RevocationStore) LookUp(v uint64) (*chainhash.Hash, error) {
  90. ind := newIndex(v)
  91. // Trying to derive the index from one of the existing buckets elements.
  92. for i := uint8(0); i < store.lenBuckets; i++ {
  93. element, err := store.buckets[i].derive(ind)
  94. if err != nil {
  95. continue
  96. }
  97. return &element.hash, nil
  98. }
  99. return nil, errors.Errorf("unable to derive hash #%v", ind)
  100. }
  101. // AddNextEntry attempts to store the given hash within its internal storage in
  102. // an efficient manner.
  103. //
  104. // NOTE: The hashes derived from the shachain MUST be inserted in the order
  105. // they're produced by a shachain.Producer.
  106. //
  107. // NOTE: This function is part of the Store interface.
  108. func (store *RevocationStore) AddNextEntry(hash *chainhash.Hash) error {
  109. newElement := &element{
  110. index: store.index,
  111. hash: *hash,
  112. }
  113. bucket := countTrailingZeros(newElement.index)
  114. for i := uint8(0); i < bucket; i++ {
  115. e, err := newElement.derive(store.buckets[i].index)
  116. if err != nil {
  117. return err
  118. }
  119. if !e.isEqual(&store.buckets[i]) {
  120. return errors.New("hash isn't derivable from " +
  121. "previous ones")
  122. }
  123. }
  124. store.buckets[bucket] = *newElement
  125. if bucket+1 > store.lenBuckets {
  126. store.lenBuckets = bucket + 1
  127. }
  128. store.index--
  129. return nil
  130. }
  131. // Encode writes a binary serialization of the shachain elements currently
  132. // saved by implementation of shachain.Store to the passed io.Writer.
  133. //
  134. // NOTE: This function is part of the Store interface.
  135. func (store *RevocationStore) Encode(w io.Writer) error {
  136. err := binary.Write(w, binary.BigEndian, store.lenBuckets)
  137. if err != nil {
  138. return err
  139. }
  140. for i := uint8(0); i < store.lenBuckets; i++ {
  141. element := store.buckets[i]
  142. err := binary.Write(w, binary.BigEndian, element.index)
  143. if err != nil {
  144. return err
  145. }
  146. if _, err = w.Write(element.hash[:]); err != nil {
  147. return err
  148. }
  149. }
  150. return binary.Write(w, binary.BigEndian, store.index)
  151. }