mirror of
https://github.com/muun/recovery.git
synced 2025-11-10 14:09:50 -05:00
Release v0.3.0
This commit is contained in:
167
vendor/github.com/lightningnetwork/lnd/shachain/element.go
generated
vendored
Normal file
167
vendor/github.com/lightningnetwork/lnd/shachain/element.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
package shachain
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// element represents the entity which contains the hash and index
|
||||
// corresponding to it. An element is the output of the shachain PRF. By
|
||||
// comparing two indexes we're able to mutate the hash in such way to derive
|
||||
// another element.
|
||||
type element struct {
|
||||
index index
|
||||
hash chainhash.Hash
|
||||
}
|
||||
|
||||
// newElementFromStr creates new element from the given hash string.
|
||||
func newElementFromStr(s string, index index) (*element, error) {
|
||||
hash, err := hashFromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &element{
|
||||
index: index,
|
||||
hash: *hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// derive computes one shachain element from another by applying a series of
|
||||
// bit flips and hashing operations based on the starting and ending index.
|
||||
func (e *element) derive(toIndex index) (*element, error) {
|
||||
fromIndex := e.index
|
||||
|
||||
positions, err := fromIndex.deriveBitTransformations(toIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := e.hash.CloneBytes()
|
||||
for _, position := range positions {
|
||||
// Flip the bit and then hash the current state.
|
||||
byteNumber := position / 8
|
||||
bitNumber := position % 8
|
||||
|
||||
buf[byteNumber] ^= (1 << bitNumber)
|
||||
|
||||
h := sha256.Sum256(buf)
|
||||
buf = h[:]
|
||||
}
|
||||
|
||||
hash, err := chainhash.NewHash(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &element{
|
||||
index: toIndex,
|
||||
hash: *hash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isEqual returns true if two elements are identical and false otherwise.
|
||||
func (e *element) isEqual(e2 *element) bool {
|
||||
return (e.index == e2.index) &&
|
||||
(&e.hash).IsEqual(&e2.hash)
|
||||
}
|
||||
|
||||
const (
|
||||
// maxHeight is used to determine the maximum allowable index and the
|
||||
// length of the array required to order to derive all previous hashes
|
||||
// by index. The entries of this array as also known as buckets.
|
||||
maxHeight uint8 = 48
|
||||
|
||||
// rootIndex is an index which corresponds to the root hash.
|
||||
rootIndex index = 0
|
||||
)
|
||||
|
||||
// startIndex is the index of first element in the shachain PRF.
|
||||
var startIndex index = (1 << maxHeight) - 1
|
||||
|
||||
// index is a number which identifies the hash number and serves as a way to
|
||||
// determine the hashing operation required to derive one hash from another.
|
||||
// index is initialized with the startIndex and decreases down to zero with
|
||||
// successive derivations.
|
||||
type index uint64
|
||||
|
||||
// newIndex is used to create index instance. The inner operations with index
|
||||
// implies that index decreasing from some max number to zero, but for
|
||||
// simplicity and backward compatibility with previous logic it was transformed
|
||||
// to work in opposite way.
|
||||
func newIndex(v uint64) index {
|
||||
return startIndex - index(v)
|
||||
}
|
||||
|
||||
// deriveBitTransformations function checks that the 'to' index is derivable
|
||||
// from the 'from' index by checking the indexes are prefixes of another. The
|
||||
// bit positions where the zeroes should be changed to ones in order for the
|
||||
// indexes to become the same are returned. This set of bits is needed in order
|
||||
// to derive one hash from another.
|
||||
//
|
||||
// NOTE: The index 'to' is derivable from index 'from' iff index 'from' lies
|
||||
// left and above index 'to' on graph below, for example:
|
||||
// 1. 7(0b111) -> 7
|
||||
// 2. 6(0b110) -> 6,7
|
||||
// 3. 5(0b101) -> 5
|
||||
// 4. 4(0b100) -> 4,5,6,7
|
||||
// 5. 3(0b011) -> 3
|
||||
// 6. 2(0b010) -> 2, 3
|
||||
// 7. 1(0b001) -> 1
|
||||
//
|
||||
// ^ bucket number
|
||||
// |
|
||||
// 3 | x
|
||||
// | |
|
||||
// 2 | | x
|
||||
// | | |
|
||||
// 1 | | x | x
|
||||
// | | | | |
|
||||
// 0 | | x | x | x | x
|
||||
// | | | | | | | | |
|
||||
// +---|---|---|---|---|---|---|---|---> index
|
||||
// 0 1 2 3 4 5 6 7
|
||||
//
|
||||
func (from index) deriveBitTransformations(to index) ([]uint8, error) {
|
||||
var positions []uint8
|
||||
|
||||
if from == to {
|
||||
return positions, nil
|
||||
}
|
||||
|
||||
// + --------------- +
|
||||
// | № | from | to |
|
||||
// + -- + ---- + --- +
|
||||
// | 48 | 1 | 1 |
|
||||
// | 47 | 0 | 0 | [48-5] - same part of 'from' and 'to'
|
||||
// | 46 | 0 | 0 | indexes which also is called prefix.
|
||||
// ....
|
||||
// | 5 | 1 | 1 |
|
||||
// | 4 | 0 | 1 | <--- position after which indexes becomes
|
||||
// | 3 | 0 | 0 | different, after this position
|
||||
// | 2 | 0 | 1 | bits in 'from' index all should be
|
||||
// | 1 | 0 | 0 | zeros or such indexes considered to be
|
||||
// | 0 | 0 | 1 | not derivable.
|
||||
// + -- + ---- + --- +
|
||||
zeros := countTrailingZeros(from)
|
||||
if uint64(from) != getPrefix(to, zeros) {
|
||||
return nil, errors.New("prefixes are different - indexes " +
|
||||
"aren't derivable")
|
||||
}
|
||||
|
||||
// The remaining part of 'to' index represents the positions which we
|
||||
// will use then in order to derive one element from another.
|
||||
for position := zeros - 1; ; position-- {
|
||||
if getBit(to, position) == 1 {
|
||||
positions = append(positions, position)
|
||||
}
|
||||
|
||||
if position == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return positions, nil
|
||||
}
|
||||
90
vendor/github.com/lightningnetwork/lnd/shachain/producer.go
generated
vendored
Normal file
90
vendor/github.com/lightningnetwork/lnd/shachain/producer.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package shachain
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// Producer is an interface which serves as an abstraction over the data
|
||||
// structure responsible for efficiently generating the secrets for a
|
||||
// particular index based on a root seed. The generation of secrets should be
|
||||
// made in such way that secret store might efficiently store and retrieve the
|
||||
// secrets. This is typically implemented as a tree-based PRF.
|
||||
type Producer interface {
|
||||
// AtIndex produces a secret by evaluating using the initial seed and a
|
||||
// particular index.
|
||||
AtIndex(uint64) (*chainhash.Hash, error)
|
||||
|
||||
// Encode writes a binary serialization of the Producer implementation
|
||||
// to the passed io.Writer.
|
||||
Encode(io.Writer) error
|
||||
}
|
||||
|
||||
// RevocationProducer is an implementation of Producer interface using the
|
||||
// shachain PRF construct. Starting with a single 32-byte element generated
|
||||
// from a CSPRNG, shachain is able to efficiently generate a nearly unbounded
|
||||
// number of secrets while maintaining a constant amount of storage. The
|
||||
// original description of shachain can be found here:
|
||||
// https://github.com/rustyrussell/ccan/blob/master/ccan/crypto/shachain/design.txt
|
||||
// with supplementary material here:
|
||||
// https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#per-commitment-secret-requirements
|
||||
type RevocationProducer struct {
|
||||
// root is the element from which we may generate all hashes which
|
||||
// corresponds to the index domain [281474976710655,0].
|
||||
root *element
|
||||
}
|
||||
|
||||
// A compile time check to ensure RevocationProducer implements the Producer
|
||||
// interface.
|
||||
var _ Producer = (*RevocationProducer)(nil)
|
||||
|
||||
// NewRevocationProducer creates new instance of shachain producer.
|
||||
func NewRevocationProducer(root chainhash.Hash) *RevocationProducer {
|
||||
return &RevocationProducer{
|
||||
root: &element{
|
||||
index: rootIndex,
|
||||
hash: root,
|
||||
}}
|
||||
}
|
||||
|
||||
// NewRevocationProducerFromBytes deserializes an instance of a
|
||||
// RevocationProducer encoded in the passed byte slice, returning a fully
|
||||
// initialized instance of a RevocationProducer.
|
||||
func NewRevocationProducerFromBytes(data []byte) (*RevocationProducer, error) {
|
||||
root, err := chainhash.NewHash(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RevocationProducer{
|
||||
root: &element{
|
||||
index: rootIndex,
|
||||
hash: *root,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AtIndex produces a secret by evaluating using the initial seed and a
|
||||
// particular index.
|
||||
//
|
||||
// NOTE: Part of the Producer interface.
|
||||
func (p *RevocationProducer) AtIndex(v uint64) (*chainhash.Hash, error) {
|
||||
ind := newIndex(v)
|
||||
|
||||
element, err := p.root.derive(ind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &element.hash, nil
|
||||
}
|
||||
|
||||
// Encode writes a binary serialization of the Producer implementation to the
|
||||
// passed io.Writer.
|
||||
//
|
||||
// NOTE: Part of the Producer interface.
|
||||
func (p *RevocationProducer) Encode(w io.Writer) error {
|
||||
_, err := w.Write(p.root.hash[:])
|
||||
return err
|
||||
}
|
||||
184
vendor/github.com/lightningnetwork/lnd/shachain/store.go
generated
vendored
Normal file
184
vendor/github.com/lightningnetwork/lnd/shachain/store.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package shachain
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/go-errors/errors"
|
||||
)
|
||||
|
||||
// Store is an interface which serves as an abstraction over data structure
|
||||
// responsible for efficiently 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)
|
||||
|
||||
// AddNextEntry attempts to store the given hash within its internal
|
||||
// storage in an efficient manner.
|
||||
//
|
||||
// NOTE: The hashes derived from the shachain MUST be inserted in the
|
||||
// order they're produced by a shachain.Producer.
|
||||
AddNextEntry(*chainhash.Hash) error
|
||||
|
||||
// Encode writes a binary serialization of the shachain elements
|
||||
// currently saved by implementation of shachain.Store to the passed
|
||||
// io.Writer.
|
||||
Encode(io.Writer) error
|
||||
}
|
||||
|
||||
// RevocationStore is a concrete implementation of the Store interface. The
|
||||
// revocation store is able to efficiently store N derived shachain elements in
|
||||
// a space efficient manner with a space complexity of O(log N). The original
|
||||
// description of the storage methodology can be found here:
|
||||
// https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#efficient-per-commitment-secret-storage
|
||||
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 with the
|
||||
// particular 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(r io.Reader) (*RevocationStore, error) {
|
||||
store := &RevocationStore{}
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &store.lenBuckets); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := uint8(0); i < store.lenBuckets; i++ {
|
||||
var hashIndex index
|
||||
err := binary.Read(r, binary.BigEndian, &hashIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nextHash chainhash.Hash
|
||||
if _, err := io.ReadFull(r, nextHash[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store.buckets[i] = element{
|
||||
index: hashIndex,
|
||||
hash: nextHash,
|
||||
}
|
||||
}
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &store.index); 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)
|
||||
}
|
||||
|
||||
// AddNextEntry attempts to store the given hash within its internal storage in
|
||||
// an efficient manner.
|
||||
//
|
||||
// NOTE: The hashes derived from the shachain MUST be inserted in the order
|
||||
// they're produced by a shachain.Producer.
|
||||
//
|
||||
// NOTE: This function is part of the Store interface.
|
||||
func (store *RevocationStore) AddNextEntry(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 derivable from " +
|
||||
"previous ones")
|
||||
}
|
||||
}
|
||||
|
||||
store.buckets[bucket] = *newElement
|
||||
if bucket+1 > store.lenBuckets {
|
||||
store.lenBuckets = bucket + 1
|
||||
}
|
||||
|
||||
store.index--
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode writes a binary serialization of the shachain elements currently
|
||||
// saved by implementation of shachain.Store to the passed io.Writer.
|
||||
//
|
||||
// NOTE: This function is part of the Store interface.
|
||||
func (store *RevocationStore) Encode(w io.Writer) error {
|
||||
err := binary.Write(w, binary.BigEndian, store.lenBuckets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := uint8(0); i < store.lenBuckets; i++ {
|
||||
element := store.buckets[i]
|
||||
|
||||
err := binary.Write(w, binary.BigEndian, element.index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = w.Write(element.hash[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return binary.Write(w, binary.BigEndian, store.index)
|
||||
}
|
||||
74
vendor/github.com/lightningnetwork/lnd/shachain/utils.go
generated
vendored
Normal file
74
vendor/github.com/lightningnetwork/lnd/shachain/utils.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package shachain
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// getBit return bit on index at position.
|
||||
func getBit(index index, position uint8) uint8 {
|
||||
return uint8((uint64(index) >> position) & 1)
|
||||
}
|
||||
|
||||
func getPrefix(index index, position uint8) uint64 {
|
||||
// + -------------------------- +
|
||||
// | № | value | mask | return |
|
||||
// + -- + ----- + ---- + ------ +
|
||||
// | 63 | 1 | 0 | 0 |
|
||||
// | 62 | 0 | 0 | 0 |
|
||||
// | 61 | 1 | 0 | 0 |
|
||||
// ....
|
||||
// | 4 | 1 | 0 | 0 |
|
||||
// | 3 | 1 | 0 | 0 |
|
||||
// | 2 | 1 | 1 | 1 | <--- position
|
||||
// | 1 | 0 | 1 | 0 |
|
||||
// | 0 | 1 | 1 | 1 |
|
||||
// + -- + ----- + ---- + ------ +
|
||||
|
||||
var zero uint64
|
||||
mask := (zero - 1) - uint64((1<<position)-1)
|
||||
return (uint64(index) & mask)
|
||||
}
|
||||
|
||||
// countTrailingZeros counts number of trailing zero bits, this function is
|
||||
// used to determine the number of element bucket.
|
||||
func countTrailingZeros(index index) uint8 {
|
||||
var zeros uint8
|
||||
for ; zeros < maxHeight; zeros++ {
|
||||
|
||||
if getBit(index, zeros) != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return zeros
|
||||
}
|
||||
|
||||
// hashFromString takes a hex-encoded string as input and creates an instance of
|
||||
// chainhash.Hash. The chainhash.NewHashFromStr function not suitable because
|
||||
// it reverse the given hash.
|
||||
func hashFromString(s string) (*chainhash.Hash, error) {
|
||||
// Return an error if hash string is too long.
|
||||
if len(s) > chainhash.MaxHashStringSize {
|
||||
return nil, chainhash.ErrHashStrSize
|
||||
}
|
||||
|
||||
// Hex decoder expects the hash to be a multiple of two.
|
||||
if len(s)%2 != 0 {
|
||||
s = "0" + s
|
||||
}
|
||||
|
||||
// Convert string hash to bytes.
|
||||
buf, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := chainhash.NewHash(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
Reference in New Issue
Block a user