diff --git a/elkrem/elkrem.go b/elkrem/elkrem.go index 6260b28c..990b14bc 100644 --- a/elkrem/elkrem.go +++ b/elkrem/elkrem.go @@ -26,30 +26,28 @@ discards leaves 0b0000 and 0b0001, as they have the parent node 8. For total hashes (2**h)-1 requires a tree of height h. Sender: -as state, must store 1 hash (root) and current index (h bits) -to move to the next index, compute at most h hashes. +as state, must store 1 hash (root) and that's all +generate any index, compute at most h hashes. Receiver: as state, must store at most h+1 hashes and the index of each hash (h*(h+1)) bits to compute a previous index, compute at most h hashes. */ +const maxIndex = uint64(281474976710654) // 2^48 - 2 +const maxHeight = uint8(47) // You can calculate h from i but I can't figure out how without taking // O(i) ops. Feels like there should be a clever O(h) way. 1 byte, whatever. type ElkremNode struct { h uint8 // height of this node - i uint64 // index (ith node) + i uint64 // index (i'th node) sha *wire.ShaHash // hash } type ElkremSender struct { - treeHeight uint8 // height of tree (size is 2**height -1 ) - current uint64 // last sent hash index - maxIndex uint64 // top of the tree - root *wire.ShaHash // root hash of the tree + root *wire.ShaHash // root hash of the tree } type ElkremReceiver struct { - treeHeight uint8 // height of tree (size is 2**height -1 ) - s []ElkremNode // store of received hashes, max size = height + s []ElkremNode // store of received hashes } func LeftSha(in wire.ShaHash) wire.ShaHash { @@ -81,43 +79,22 @@ func descend(w, i uint64, h uint8, sha wire.ShaHash) (wire.ShaHash, error) { return sha, nil } -// Creates an Elkrem Sender from a root hash and tree height -func NewElkremSender(th uint8, r wire.ShaHash) ElkremSender { +// Creates an Elkrem Sender from a root hash. +func NewElkremSender(r wire.ShaHash) *ElkremSender { var e ElkremSender e.root = &r - e.treeHeight = th - // set max index based on tree height - for j := uint8(0); j <= e.treeHeight; j++ { - e.maxIndex = e.maxIndex<<1 | 1 - } - e.maxIndex-- // 1 less than 2**h - return e + return &e } -// Creates an Elkrem Receiver from a tree height -func NewElkremReceiver(th uint8) ElkremReceiver { - var e ElkremReceiver - e.treeHeight = th - return e -} - -// Next() increments the index to the next hash and outputs it -func (e *ElkremSender) Next() (*wire.ShaHash, error) { - sha, err := e.AtIndex(e.current) - if err != nil { - return nil, err - } - // increment index for next time - e.current++ - return sha, nil -} - -// w is the wanted index, i is the root index +// AtIndex skips to the requested index +// should never error; remove error..? func (e *ElkremSender) AtIndex(w uint64) (*wire.ShaHash, error) { - out, err := descend(w, e.maxIndex, e.treeHeight, *e.root) + out, err := descend(w, maxIndex, maxHeight, *e.root) return &out, err } +// AddNext inserts the next hash in the tree. Returns an error if +// the incoming hash doesn't fit. func (e *ElkremReceiver) AddNext(sha *wire.ShaHash) error { // note: careful about atomicity / disk writes here var n ElkremNode @@ -144,7 +121,12 @@ func (e *ElkremReceiver) AddNext(sha *wire.ShaHash) error { e.s = append(e.s, n) // append new node to stack return nil } + +// AtIndex returns the w'th hash in the receiver. func (e *ElkremReceiver) AtIndex(w uint64) (*wire.ShaHash, error) { + if e == nil || e.s == nil { + return nil, fmt.Errorf("nil elkrem receiver") + } var out ElkremNode // node we will eventually return for _, n := range e.s { // go through stack if w <= n.i { // found one bigger than or equal to what we want @@ -159,3 +141,11 @@ func (e *ElkremReceiver) AtIndex(w uint64) (*wire.ShaHash, error) { sha, err := descend(w, out.i, out.h, *out.sha) return &sha, err } + +// UpTo tells you what the receiver can go up to. +func (e *ElkremReceiver) UpTo() uint64 { + if len(e.s) < 1 { + return 0 + } + return e.s[len(e.s)-1].i +} diff --git a/elkrem/elkrem_test.go b/elkrem/elkrem_test.go index 937e8648..f8646344 100644 --- a/elkrem/elkrem_test.go +++ b/elkrem/elkrem_test.go @@ -6,13 +6,13 @@ import ( "github.com/roasbeef/btcd/wire" ) -// TestElkremBig makes a height 63 (max size possible) tree and tries 10K hashes +// TestElkremBig tries 10K hashes func TestElkremBig(t *testing.T) { - sndr := NewElkremSender(63, wire.DoubleSha256SH([]byte("elktest"))) - rcv := NewElkremReceiver(63) - SenderSerdesTest(t, &sndr) + sndr := NewElkremSender(wire.DoubleSha256SH([]byte("elktest"))) + var rcv ElkremReceiver + // SenderSerdesTest(t, sndr) for n := uint64(0); n < 10000; n++ { - sha, err := sndr.Next() + sha, err := sndr.AtIndex(n) if err != nil { t.Fatal(err) } @@ -28,7 +28,7 @@ func TestElkremBig(t *testing.T) { } } } - SenderSerdesTest(t, &sndr) + // SenderSerdesTest(t, sndr) ReceiverSerdesTest(t, &rcv) for n := uint64(0); n < 10000; n += 500 { sha, err := rcv.AtIndex(n) @@ -39,12 +39,12 @@ func TestElkremBig(t *testing.T) { } } -// TestElkremSmall makes a height 15 (65534 size) tree and tries 10K hashes -func TestElkremSmall(t *testing.T) { - sndr := NewElkremSender(15, wire.DoubleSha256SH([]byte("elktest"))) - rcv := NewElkremReceiver(15) +// TestElkremLess tries 10K hashes +func TestElkremLess(t *testing.T) { + sndr := NewElkremSender(wire.DoubleSha256SH([]byte("elktest2"))) + var rcv ElkremReceiver for n := uint64(0); n < 5000; n++ { - sha, err := sndr.Next() + sha, err := sndr.AtIndex(n) if err != nil { t.Fatal(err) } diff --git a/elkrem/findpre.go b/elkrem/findpre.go deleted file mode 100644 index 6b533041..00000000 --- a/elkrem/findpre.go +++ /dev/null @@ -1,76 +0,0 @@ -package elkrem - -import ( - "bytes" - "fmt" - - "github.com/roasbeef/btcutil" -) - -/* findpre - find the pre-image for a given hash - -The worst (?) has happened and your channel counterparty has broadcast an old, -invalid state. That's bad. But what, that means you get to take all the money. -This is the fun part of the channel enforcement mechanism. - -The old transaction they broadcast has an nLockTime field which provides a hint -about which state number, and which hash index, was used. Bitcoin's nLockTime -field counts as blocks from 0 to 499,999,999 (which would happen somtime after -10,000 CE) and as a unix time from 500,000,000 up to 4,294,967,296 (which is -much sooner, in 2106). There is some extra space we can use here in both cases, -though the unix time gives us much more. nLockTimes from 500,000,000 to -1,036,870,912 are safe, representing dates from 1985 to 2002, which are before -any possible bitcoin transaction. 1,036,870,912 is 500,000,000 plus -536,870,912, and 536,870,912 is 2^29, so we have 29 bits of free space. - -After subtracting 500,000,000, the remaning lowest 29 bits are the bits of the -index within the elkrem receiver used in that transaction. That way, even with -a trillion previous channel states (2^40) we will only need to search through -2048 possible branches to find the right pre-image. In most cases, there will -be fewer than 536,870,912 previous states and we can seek directly to the -correct pre-image. -*/ - -func (e *ElkremReceiver) FindPre( - target [20]byte, timeHint uint32) (*[20]byte, error) { - - maxUint32 := uint32((1 << 32) - 1) - minTime := uint32(500000000) - hintRange := uint32((1 << 29) - 1) - - // a timeHint of 2^32 (4294967296) means we don't have a timeHint. - if timeHint == maxUint32 { - return nil, fmt.Errorf("no timeHint") - } - // valid timeHint range is 500M to 500M + 2^29 - if timeHint < minTime || timeHint > minTime+hintRange { - return nil, fmt.Errorf("timeHint %d out of range (500M - ~1G)", timeHint) - } - indexHint := uint64(timeHint - minTime) - maxIndex := e.s[len(e.s)-1].i // highest index we have - - if indexHint > maxIndex { // we can't derive needed index - return nil, fmt.Errorf("hint index %d greater than max index %d", - indexHint, maxIndex) - } - - // iterate though, adding 2^29 each time. - // there is some redundancy here when you have a large number of guesses - // to go through, so this could be optimized later. - for guess := indexHint; guess < maxIndex; guess += uint64(hintRange) { - sha, err := e.AtIndex(guess) // generate preimage - if err != nil { - return nil, err - } - var truncatedSha [20]byte - copy(truncatedSha[:], sha.Bytes()) // copy into 20 byte array - - checkHash := btcutil.Hash160(truncatedSha[:]) // hash and compare - if bytes.Equal(target[:], checkHash) { // matches hash, return - return &truncatedSha, nil - } - } - // got through the loop without finding anything. - return nil, fmt.Errorf("Couldn't find preimage of %x. timeHint %d bad?", - target, timeHint) -} diff --git a/elkrem/serdes.go b/elkrem/serdes.go index e8b2728d..0b318be3 100644 --- a/elkrem/serdes.go +++ b/elkrem/serdes.go @@ -10,97 +10,27 @@ import ( /* Serialization and Deserialization methods for the Elkrem structs. Senders turn into 41 byte long slices. Receivers are variable length, -with 41 bytes for each stored hash, up to a maximum of 64. Receivers are -prepended with the total number of hashes, so the total max size is 2625 bytes. +with 41 bytes for each stored hash, up to a maximum of 48. Receivers are +prepended with the total number of hashes, so the total max size is 1969 bytes. */ -// ToBytes turns the Elkrem Sender into a 41 byte slice: -// first the tree height (1 byte), then 8 byte index of last sent, -// then the 32 byte root sha hash. -func (e *ElkremSender) ToBytes() ([]byte, error) { - var buf bytes.Buffer - // write 1 byte height of tree (size of the whole sender) - err := binary.Write(&buf, binary.BigEndian, e.treeHeight) - if err != nil { - return nil, err - } - // write 8 byte index of current sha (last sent) - err = binary.Write(&buf, binary.BigEndian, e.current) - if err != nil { - return nil, err - } - // write 32 byte sha hash - n, err := buf.Write(e.root.Bytes()) - if err != nil { - return nil, err - } - if n != 32 { - return nil, fmt.Errorf("%d byte hash, expect 32", n) - } - - return buf.Bytes(), nil -} - -// ElkremSenderFromBytes turns a 41 byte slice into a sender, picking up at -// the index where it left off. -func ElkremSenderFromBytes(b []byte) (ElkremSender, error) { - var e ElkremSender - e.root = new(wire.ShaHash) - buf := bytes.NewBuffer(b) - if buf.Len() != 41 { - return e, fmt.Errorf("Got %d bytes for sender, expect 41") - } - // read 1 byte height - err := binary.Read(buf, binary.BigEndian, &e.treeHeight) - if err != nil { - return e, err - } - // read 8 byte index - err = binary.Read(buf, binary.BigEndian, &e.current) - if err != nil { - return e, err - } - // read 32 byte sha root - err = e.root.SetBytes(buf.Next(32)) - if err != nil { - return e, err - } - if e.treeHeight < 1 || e.treeHeight > 63 { // check for super high / low tree - return e, fmt.Errorf("Read invalid sender tree height %d", e.treeHeight) - } - for j := uint8(0); j <= e.treeHeight; j++ { - e.maxIndex = e.maxIndex<<1 | 1 - } - e.maxIndex-- - - if e.current > e.maxIndex { // check for index higher than height allows - return e, fmt.Errorf("Sender claims current %d; %d max with height %d", - e.current, e.maxIndex, e.treeHeight) - } - return e, nil -} - // ToBytes turns the Elkrem Receiver into a bunch of bytes in a slice. -// first the tree height (1 byte), then number of nodes (1 byte), -// then a series of 41 byte long serialized nodes, -// which are 1 byte height, 8 byte index, 32 byte hash. +// first the number of nodes (1 byte), then a series of 41 byte long +// serialized nodes, which are 1 byte height, 8 byte index, 32 byte hash. func (e *ElkremReceiver) ToBytes() ([]byte, error) { numOfNodes := uint8(len(e.s)) + // 0 element receiver also OK. Just an empty slice. if numOfNodes == 0 { - return nil, fmt.Errorf("Can't serialize empty ElkremReceiver") + return nil, nil } - if numOfNodes > 64 { + if numOfNodes > maxHeight+1 { return nil, fmt.Errorf("Broken ElkremReceiver has %d nodes, max 64", len(e.s)) } var buf bytes.Buffer // create buffer - // write tree height (1 byte) - err := binary.Write(&buf, binary.BigEndian, e.treeHeight) - if err != nil { - return nil, err - } + // write number of nodes (1 byte) - err = binary.Write(&buf, binary.BigEndian, numOfNodes) + err := binary.Write(&buf, binary.BigEndian, numOfNodes) if err != nil { return nil, err } @@ -115,6 +45,9 @@ func (e *ElkremReceiver) ToBytes() ([]byte, error) { if err != nil { return nil, err } + if node.sha == nil { + return nil, fmt.Errorf("node %d has nil hash", node.i) + } // write 32 byte sha hash n, err := buf.Write(node.sha.Bytes()) if err != nil { @@ -124,78 +57,119 @@ func (e *ElkremReceiver) ToBytes() ([]byte, error) { return nil, fmt.Errorf("%d byte hash, expect 32", n) } } - if buf.Len() != (int(numOfNodes)*41)+2 { + if buf.Len() != (int(numOfNodes)*41)+1 { return nil, fmt.Errorf("Somehow made wrong size buf, got %d expect %d", - buf.Len(), (numOfNodes*41)+2) + buf.Len(), (numOfNodes*41)+1) } return buf.Bytes(), nil } -func ElkremReceiverFromBytes(b []byte) (ElkremReceiver, error) { +func ElkremReceiverFromBytes(b []byte) (*ElkremReceiver, error) { var e ElkremReceiver - var numOfNodes uint8 + if len(b) == 0 { // empty receiver, which is OK + return &e, nil + } buf := bytes.NewBuffer(b) - // read 1 byte tree height - err := binary.Read(buf, binary.BigEndian, &e.treeHeight) - if err != nil { - return e, err - } - if e.treeHeight < 1 || e.treeHeight > 63 { - return e, fmt.Errorf("Read invalid receiver height: %d", e.treeHeight) - } - var max uint64 // maximum possible given height - for j := uint8(0); j <= e.treeHeight; j++ { - max = max<<1 | 1 - } - max-- - // read 1 byte number of nodes stored in receiver - err = binary.Read(buf, binary.BigEndian, &numOfNodes) + numOfNodes, err := buf.ReadByte() if err != nil { - return e, err + return nil, err } - if numOfNodes < 1 || numOfNodes > 64 { - return e, fmt.Errorf("Read invalid number of nodes: %d", numOfNodes) + if numOfNodes < 1 || numOfNodes > maxHeight+1 { + return nil, fmt.Errorf("Read invalid number of nodes: %d", numOfNodes) } - if buf.Len() != (int(numOfNodes) * 41) { - return e, fmt.Errorf("Remaining buf wrong size, expect %d got %d", + return nil, fmt.Errorf("Remaining buf wrong size, expect %d got %d", (numOfNodes * 41), buf.Len()) } - for i := uint8(0); i < numOfNodes; i++ { - var node ElkremNode - node.sha = new(wire.ShaHash) + e.s = make([]ElkremNode, numOfNodes) + + for j, _ := range e.s { + e.s[j].sha = new(wire.ShaHash) // read 1 byte height - err := binary.Read(buf, binary.BigEndian, &node.h) + err := binary.Read(buf, binary.BigEndian, &e.s[j].h) if err != nil { - return e, err + return nil, err } // read 8 byte index - err = binary.Read(buf, binary.BigEndian, &node.i) + err = binary.Read(buf, binary.BigEndian, &e.s[j].i) if err != nil { - return e, err + return nil, err } // read 32 byte sha hash - err = node.sha.SetBytes(buf.Next(32)) + err = e.s[j].sha.SetBytes(buf.Next(32)) if err != nil { - return e, err + return nil, err } // sanity check. Note that this doesn't check that index and height // match. Could add that but it's slow. - if node.h > 63 { // check for super high nodes - return e, fmt.Errorf("Read invalid node height %d", node.h) + if e.s[j].h > maxHeight { // check for super high nodes + return nil, fmt.Errorf("Read invalid node height %d", e.s[j].h) } - if node.i > max { // check for index higher than height allows - return e, fmt.Errorf("Node claims index %d; %d max at height %d", - node.i, max, node.h) + if e.s[j].i > maxIndex { // check for index higher than height allows + return nil, fmt.Errorf("Node claims index %d; %d max at height %d", + e.s[j].i, maxIndex, e.s[j].h) } - e.s = append(e.s, node) - if i > 0 { // check that node heights are descending - if e.s[i-1].h < e.s[i].h { - return e, fmt.Errorf("Node heights out of order") + + if j > 0 { // check that node heights are descending + if e.s[j-1].h < e.s[j].h { + return nil, fmt.Errorf("Node heights out of order") } } } - return e, nil + return &e, nil } + +// There's no real point to the *sender* serialization because +// you just make them from scratch each time. Only thing to save +// is the 32 byte seed and the current index. + +// ToBytes turns the Elkrem Sender into a 41 byte slice: +// first the tree height (1 byte), then 8 byte index of last sent, +// then the 32 byte root sha hash. +//func (e *ElkremSender) ToBytes() ([]byte, error) { +// var buf bytes.Buffer +// // write 8 byte index of current sha (last sent) +// err := binary.Write(&buf, binary.BigEndian, e.current) +// if err != nil { +// return nil, err +// } +// // write 32 byte sha hash +// n, err := buf.Write(e.root.Bytes()) +// if err != nil { +// return nil, err +// } +// if n != 32 { +// return nil, fmt.Errorf("%d byte hash, expect 32", n) +// } + +// return buf.Bytes(), nil +//} + +// ElkremSenderFromBytes turns a 41 byte slice into a sender, picking up at +// the index where it left off. +//func ElkremSenderFromBytes(b []byte) (ElkremSender, error) { +// var e ElkremSender +// e.root = new(wire.ShaHash) +// buf := bytes.NewBuffer(b) +// if buf.Len() != 40 { +// return e, fmt.Errorf("Got %d bytes for sender, expect 41") +// } +// // read 8 byte index +// err := binary.Read(buf, binary.BigEndian, &e.current) +// if err != nil { +// return e, err +// } +// // read 32 byte sha root +// err = e.root.SetBytes(buf.Next(32)) +// if err != nil { +// return e, err +// } + +// if e.current > maxIndex { // check for index higher than height allows +// return e, fmt.Errorf("Sender claims current %d; %d max with height %d", +// e.current, maxIndex, maxHeight) +// } +// return e, nil +//} diff --git a/elkrem/serdes_test.go b/elkrem/serdes_test.go index 3027ce59..e01c03a2 100644 --- a/elkrem/serdes_test.go +++ b/elkrem/serdes_test.go @@ -10,14 +10,15 @@ func ReceiverSerdesTest(t *testing.T, rcv *ElkremReceiver) { if err != nil { t.Fatal(err) } + t.Logf("rcv2 has len %d\n", len(rcv.s)) t.Logf("Serialized receiver; %d bytes, hex:\n%x\n", len(b), b) - *rcv, err = ElkremReceiverFromBytes(b) + rcv2, err := ElkremReceiverFromBytes(b) if err != nil { t.Fatal(err) } - - b2, err := rcv.ToBytes() + t.Logf("rcv2 has len %d\n", len(rcv2.s)) + b2, err := rcv2.ToBytes() if err != nil { t.Fatal(err) } @@ -27,24 +28,24 @@ func ReceiverSerdesTest(t *testing.T, rcv *ElkremReceiver) { } } -func SenderSerdesTest(t *testing.T, sndr *ElkremSender) { - b, err := sndr.ToBytes() - if err != nil { - t.Fatal(err) - } - t.Logf("Serialized sender; %d bytes, hex:\n%x\n", len(b), b) +//func SenderSerdesTest(t *testing.T, sndr *ElkremSender) { +// b, err := sndr.ToBytes() +// if err != nil { +// t.Fatal(err) +// } +// t.Logf("Serialized sender; %d bytes, hex:\n%x\n", len(b), b) - *sndr, err = ElkremSenderFromBytes(b) - if err != nil { - t.Fatal(err) - } +// *sndr, err = ElkremSenderFromBytes(b) +// if err != nil { +// t.Fatal(err) +// } - b2, err := sndr.ToBytes() - if err != nil { - t.Fatal(err) - } +// b2, err := sndr.ToBytes() +// if err != nil { +// t.Fatal(err) +// } - if !bytes.Equal(b, b2) { - t.Fatalf("First and second serializations different") - } -} +// if !bytes.Equal(b, b2) { +// t.Fatalf("First and second serializations different") +// } +//}