mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
187 lines
4.8 KiB
Go
187 lines
4.8 KiB
Go
|
package shachain
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"github.com/go-errors/errors"
|
||
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||
|
)
|
||
|
|
||
|
// Store is an interface which serves as an abstraction over data structure
|
||
|
// responsible for efficient storing and restoring of hash secrets by given
|
||
|
// indexes.
|
||
|
//
|
||
|
// Description: The Lightning Network wants a chain of (say 1 million)
|
||
|
// unguessable 256 bit values; we generate them and send them one at a time
|
||
|
// to a remote node. We don't want the remote node to have to store all the
|
||
|
// values, so it's better if they can derive them once they see them.
|
||
|
type Store interface {
|
||
|
// LookUp function is used to restore/lookup/fetch the previous secret
|
||
|
// by its index.
|
||
|
LookUp(uint64) (*chainhash.Hash, error)
|
||
|
|
||
|
// Store is used to store the given sha hash in efficient manner.
|
||
|
Store(*chainhash.Hash) error
|
||
|
|
||
|
// ToBytes convert store to the binary representation.
|
||
|
ToBytes() ([]byte, error)
|
||
|
}
|
||
|
|
||
|
// RevocationStore implementation of SecretStore. This version of shachain store
|
||
|
// slightly changed in terms of method naming. Initial concept might be found
|
||
|
// here:
|
||
|
// https://github.com/rustyrussell/ccan/blob/master/ccan/crypto/shachain/design.txt
|
||
|
type RevocationStore struct {
|
||
|
// lenBuckets stores the number of currently active buckets.
|
||
|
lenBuckets uint8
|
||
|
|
||
|
// buckets is an array of elements from which we may derive all previous
|
||
|
// elements, each bucket corresponds to the element index number of
|
||
|
// trailing zeros.
|
||
|
buckets [maxHeight]element
|
||
|
|
||
|
// index is an available index which will be assigned to the new
|
||
|
// element.
|
||
|
index index
|
||
|
}
|
||
|
|
||
|
// A compile time check to ensure RevocationStore implements the Store
|
||
|
// interface.
|
||
|
var _ Store = (*RevocationStore)(nil)
|
||
|
|
||
|
// NewRevocationStore creates the new shachain store.
|
||
|
func NewRevocationStore() *RevocationStore {
|
||
|
return &RevocationStore{
|
||
|
lenBuckets: 0,
|
||
|
index: startIndex,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewRevocationStoreFromBytes recreates the initial store state from the given
|
||
|
// binary shachain store representation.
|
||
|
func NewRevocationStoreFromBytes(data []byte) (*RevocationStore, error) {
|
||
|
var err error
|
||
|
|
||
|
store := &RevocationStore{}
|
||
|
buf := bytes.NewBuffer(data)
|
||
|
|
||
|
err = binary.Read(buf, binary.BigEndian, &store.lenBuckets)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
i := uint8(0)
|
||
|
for ; i < store.lenBuckets; i++ {
|
||
|
e := &element{}
|
||
|
|
||
|
err = binary.Read(buf, binary.BigEndian, &e.index)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
hash, err := chainhash.NewHash(buf.Next(chainhash.HashSize))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
e.hash = *hash
|
||
|
store.buckets[i] = *e
|
||
|
}
|
||
|
|
||
|
err = binary.Read(buf, binary.BigEndian, &store.index)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return store, nil
|
||
|
}
|
||
|
|
||
|
// LookUp function is used to restore/lookup/fetch the previous secret by its
|
||
|
// index. If secret which corresponds to given index was not previously placed
|
||
|
// in store we will not able to derive it and function will fail.
|
||
|
//
|
||
|
// NOTE: This function is part of the Store interface.
|
||
|
func (store *RevocationStore) LookUp(v uint64) (*chainhash.Hash, error) {
|
||
|
ind := newIndex(v)
|
||
|
|
||
|
// Trying to derive the index from one of the existing buckets elements.
|
||
|
for i := uint8(0); i < store.lenBuckets; i++ {
|
||
|
element, err := store.buckets[i].derive(ind)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
return &element.hash, nil
|
||
|
}
|
||
|
|
||
|
return nil, errors.Errorf("unable to derive hash #%v", ind)
|
||
|
}
|
||
|
|
||
|
// Store is used to store the given sha hash in efficient manner. Given hash
|
||
|
// should be computable with previous ones, and derived from the previous index
|
||
|
// otherwise the function will return the error.
|
||
|
//
|
||
|
// NOTE: This function is part of the Store interface.
|
||
|
func (store *RevocationStore) Store(hash *chainhash.Hash) error {
|
||
|
newElement := &element{
|
||
|
index: store.index,
|
||
|
hash: *hash,
|
||
|
}
|
||
|
|
||
|
bucket := countTrailingZeros(newElement.index)
|
||
|
|
||
|
for i := uint8(0); i < bucket; i++ {
|
||
|
e, err := newElement.derive(store.buckets[i].index)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if !e.isEqual(&store.buckets[i]) {
|
||
|
return errors.New("hash isn't deriavable from " +
|
||
|
"previous ones")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
store.buckets[bucket] = *newElement
|
||
|
if bucket+1 > store.lenBuckets {
|
||
|
store.lenBuckets = bucket + 1
|
||
|
}
|
||
|
|
||
|
store.index--
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ToBytes convert store to the binary representation.
|
||
|
// NOTE: This function is part of the Store interface.
|
||
|
func (store *RevocationStore) ToBytes() ([]byte, error) {
|
||
|
var buf bytes.Buffer
|
||
|
var err error
|
||
|
|
||
|
err = binary.Write(&buf, binary.BigEndian, store.lenBuckets)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
i := uint8(0)
|
||
|
for ; i < store.lenBuckets; i++ {
|
||
|
element := store.buckets[i]
|
||
|
|
||
|
err = binary.Write(&buf, binary.BigEndian, element.index)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
_, err = buf.Write(element.hash.CloneBytes())
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
err = binary.Write(&buf, binary.BigEndian, store.index)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return buf.Bytes(), nil
|
||
|
}
|