2015-12-24 21:42:03 +03:00
|
|
|
package shachain
|
2015-11-19 01:59:51 +03:00
|
|
|
|
2015-12-16 23:51:59 +03:00
|
|
|
import (
|
2015-12-17 03:36:03 +03:00
|
|
|
"bytes"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
|
|
|
"fmt"
|
2015-12-24 21:48:24 +03:00
|
|
|
"io"
|
2015-12-16 23:51:59 +03:00
|
|
|
"sync"
|
|
|
|
|
2016-05-15 17:17:44 +03:00
|
|
|
"github.com/roasbeef/btcd/wire"
|
|
|
|
"github.com/roasbeef/btcutil"
|
2015-12-16 23:51:59 +03:00
|
|
|
)
|
2015-11-19 01:59:51 +03:00
|
|
|
|
2015-12-17 03:36:03 +03:00
|
|
|
const (
|
|
|
|
maxIndex = 1<<64 - 1
|
|
|
|
)
|
|
|
|
|
2015-11-19 01:59:51 +03:00
|
|
|
// chainFragment...
|
2015-12-17 03:36:03 +03:00
|
|
|
type chainBranch struct {
|
2015-11-19 01:59:51 +03:00
|
|
|
index uint64
|
2015-12-17 03:36:03 +03:00
|
|
|
hash [32]byte
|
2015-11-19 01:59:51 +03:00
|
|
|
}
|
|
|
|
|
2015-12-16 23:43:59 +03:00
|
|
|
// HyperShaChain...
|
2015-12-16 23:49:01 +03:00
|
|
|
// * https://github.com/rustyrussell/ccan/blob/master/ccan/crypto/shachain/design.txt
|
2015-12-16 23:43:59 +03:00
|
|
|
type HyperShaChain struct {
|
2015-12-16 23:51:59 +03:00
|
|
|
sync.RWMutex
|
|
|
|
|
2015-11-19 01:59:51 +03:00
|
|
|
lastChainIndex uint64
|
2015-12-17 03:36:03 +03:00
|
|
|
numValid uint64
|
2015-11-19 01:59:51 +03:00
|
|
|
|
2015-12-17 03:36:03 +03:00
|
|
|
chainBranches [64]chainBranch
|
|
|
|
|
|
|
|
lastHash wire.ShaHash
|
2015-11-19 01:59:51 +03:00
|
|
|
}
|
|
|
|
|
2015-12-17 03:42:07 +03:00
|
|
|
// NewHyperShaChain
|
|
|
|
// * used to track their pre-images
|
2015-12-24 21:42:03 +03:00
|
|
|
func New() *HyperShaChain {
|
2015-12-17 03:42:07 +03:00
|
|
|
return &HyperShaChain{lastChainIndex: 0, numValid: 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHyperShaChainFromSeed...
|
|
|
|
// * used to derive your own pre-images
|
2015-12-24 21:42:03 +03:00
|
|
|
func NewFromSeed(seed *[32]byte, deriveTo uint64) (*HyperShaChain, error) {
|
2015-12-22 00:41:23 +03:00
|
|
|
var shaSeed [32]byte
|
2015-12-17 03:36:03 +03:00
|
|
|
|
|
|
|
// If no seed is specified, generate a new one.
|
|
|
|
if seed == nil {
|
|
|
|
_, err := rand.Read(shaSeed[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-12-22 00:41:23 +03:00
|
|
|
} else {
|
|
|
|
shaSeed = *seed
|
2015-12-17 03:36:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// The last possible value in the chain is our starting index.
|
|
|
|
start := uint64(maxIndex)
|
|
|
|
stop := deriveTo
|
|
|
|
|
2015-12-22 00:41:23 +03:00
|
|
|
curHash := derive(start, stop, shaSeed)
|
2015-12-17 03:36:03 +03:00
|
|
|
|
2015-11-19 01:59:51 +03:00
|
|
|
// TODO(roasbeef): from/to or static size?
|
2015-12-17 03:36:03 +03:00
|
|
|
return &HyperShaChain{lastChainIndex: deriveTo, lastHash: curHash}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// derive...
|
|
|
|
func derive(from, to uint64, startingHash [32]byte) [32]byte {
|
|
|
|
nextHash := startingHash
|
|
|
|
|
|
|
|
numBranches := from ^ to
|
2015-12-23 21:12:53 +03:00
|
|
|
|
|
|
|
// The number of branches we need to derive is log2(numBranches)
|
|
|
|
toDerive := 0
|
|
|
|
for ; numBranches>>uint(toDerive) > 0; toDerive++ {
|
2015-12-22 06:36:28 +03:00
|
|
|
}
|
|
|
|
toDerive-- // needed?
|
|
|
|
|
2015-12-22 00:47:36 +03:00
|
|
|
for i := int(toDerive - 1); i >= 0; i-- {
|
|
|
|
if (numBranches>>uint(i))&1 == 1 {
|
2015-12-17 03:36:03 +03:00
|
|
|
// Flip the ith bit, then hash the current state to
|
|
|
|
// advance down the tree.
|
2015-12-22 00:47:36 +03:00
|
|
|
nextHash[i/8] ^= (1 << (uint(i) % 8))
|
2015-12-17 03:36:03 +03:00
|
|
|
nextHash = sha256.Sum256(nextHash[:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nextHash
|
|
|
|
}
|
|
|
|
|
2015-12-17 03:42:07 +03:00
|
|
|
// canDerive...
|
|
|
|
func canDerive(from, to uint64) bool {
|
|
|
|
return ^from&to == 1
|
|
|
|
}
|
|
|
|
|
2015-12-17 03:36:03 +03:00
|
|
|
// getHash...
|
|
|
|
// index should be commitment #
|
|
|
|
func (h *HyperShaChain) GetHash(index uint64) (*[32]byte, error) {
|
|
|
|
for i := uint64(0); i < h.numValid; i++ {
|
|
|
|
/* If we can get from key to index only by resetting bits,
|
|
|
|
* we can derive from it => index has no bits key doesn't. */
|
|
|
|
if !canDerive(h.chainBranches[i].index, index) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nextHash := derive(h.chainBranches[i].index, index,
|
|
|
|
h.chainBranches[i].hash)
|
|
|
|
|
|
|
|
return &nextHash, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unable to derive hash # %v", index)
|
|
|
|
}
|
|
|
|
|
|
|
|
// addHash
|
|
|
|
func (h *HyperShaChain) AddNextHash(hash [32]byte) error {
|
|
|
|
// Hashes for a remote chain must be added in order.
|
2015-12-17 03:42:07 +03:00
|
|
|
nextIdx := h.lastChainIndex + 1
|
2015-12-17 03:36:03 +03:00
|
|
|
if nextIdx != h.lastChainIndex+1 || nextIdx == 0 && h.numValid != 0 {
|
|
|
|
return fmt.Errorf("shachain values must be added in order, attempted"+
|
|
|
|
"to add index %v, chain is at %v", nextIdx, h.lastChainIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
i := uint64(0)
|
|
|
|
for ; i < h.numValid; i++ {
|
|
|
|
if canDerive(nextIdx, h.chainBranches[i].index) {
|
|
|
|
// Ensure we can actually derive this value.
|
|
|
|
derivation := derive(nextIdx, h.chainBranches[i].index, hash)
|
|
|
|
if !bytes.Equal(derivation[:], h.chainBranches[i].hash[:]) {
|
|
|
|
// TODO(roasbeef): better err message
|
|
|
|
return fmt.Errorf("chain corruption")
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
h.chainBranches[i].index = nextIdx
|
|
|
|
copy(h.chainBranches[i].hash[:], hash[:])
|
|
|
|
copy(h.lastHash[:], hash[:])
|
|
|
|
h.numValid = i + 1
|
|
|
|
h.lastChainIndex = nextIdx
|
2015-12-16 23:39:05 +03:00
|
|
|
return nil
|
2015-11-19 01:59:51 +03:00
|
|
|
}
|
|
|
|
|
2015-12-21 06:46:41 +03:00
|
|
|
// CurrentPreImage...
|
|
|
|
func (h *HyperShaChain) CurrentPreImage() *wire.ShaHash {
|
|
|
|
h.RLock()
|
|
|
|
defer h.RUnlock()
|
|
|
|
return &h.lastHash
|
|
|
|
}
|
|
|
|
|
|
|
|
// CurrentRevocationHash...
|
|
|
|
// TODO(roasbeef): *wire.ShaHash vs [wire.HashSize]byte ?
|
2015-12-31 09:30:00 +03:00
|
|
|
func (h *HyperShaChain) CurrentRevocationHash() []byte {
|
2015-12-21 06:46:41 +03:00
|
|
|
h.RLock()
|
|
|
|
defer h.RUnlock()
|
2015-12-31 09:30:00 +03:00
|
|
|
return btcutil.Hash160(h.lastHash[:])
|
2015-12-17 03:36:03 +03:00
|
|
|
}
|
|
|
|
|
2015-12-17 07:56:11 +03:00
|
|
|
// LocatePreImage...
|
|
|
|
// Alice just broadcasted an old commitment tx, we need the revocation hash to
|
|
|
|
// claim the funds so we don't get cheated. However, we aren't storing all the
|
|
|
|
// pre-images in memory. So which shachain index # did she broadcast?
|
|
|
|
func (h *HyperShaChain) LocatePreImage(outputScript []byte) (uint64, *[32]byte) {
|
|
|
|
// TODO(roasbeef): parallel goroutine divide and conquer?
|
|
|
|
// * need to know which side it is? also proper keys?
|
|
|
|
// * guess and check till script template matches the p2sh hash
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
2015-12-17 03:36:03 +03:00
|
|
|
// MarshallBinary...
|
2015-12-24 21:48:24 +03:00
|
|
|
func (h *HyperShaChain) Encode(b io.Writer) error {
|
2015-12-17 03:36:03 +03:00
|
|
|
return nil
|
2015-11-19 01:59:51 +03:00
|
|
|
}
|
|
|
|
|
2015-12-17 03:36:03 +03:00
|
|
|
// UnmarshallBinary...
|
2015-12-24 21:48:24 +03:00
|
|
|
func (h *HyperShaChain) Decode(b io.Reader) error {
|
2015-12-17 03:36:03 +03:00
|
|
|
return nil
|
2015-11-19 01:59:51 +03:00
|
|
|
}
|