diff --git a/channeldb/channel.go b/channeldb/channel.go index 9a52b494..b513c48a 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -9,9 +9,8 @@ import ( "time" "github.com/boltdb/bolt" - "github.com/lightningnetwork/lnd/elkrem" + "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" ) @@ -89,9 +88,9 @@ var ( // and finally 2-of-2 multisig redeem script. fundingTxnKey = []byte("fsk") - // elkremStateKey stores their current revocation hash, and our elkrem - // sender, and their elkrem receiver. - elkremStateKey = []byte("esk") + // preimageStateKey stores their current revocation hash, our + // preimage producer and their preimage store. + preimageStateKey = []byte("esk") // deliveryScriptsKey stores the scripts for the final delivery in the // case of a cooperative closure. @@ -225,8 +224,17 @@ type OpenChannel struct { // aren't yet able to verify that it's actually in the hash chain. TheirCurrentRevocation *btcec.PublicKey TheirCurrentRevocationHash [32]byte - LocalElkrem *elkrem.ElkremSender - RemoteElkrem *elkrem.ElkremReceiver + + // RevocationProducer is used to generate the revocation in such a way + // that remote side might store it efficiently and have the ability to + // restore the revocation by index if needed. Current implementation of + // secret producer is shachain producer. + RevocationProducer shachain.Producer + + // RevocationStore is used to efficiently store the revocations for + // previous channels states sent to us by remote side. Current + // implementation of secret store is shachain store. + RevocationStore shachain.Store // OurDeliveryScript is the script to be used to pay to us in // cooperative closes. @@ -480,15 +488,16 @@ func (c *OpenChannel) AppendToRevocationLog(delta *ChannelDelta) error { return err } - // Persist the latest elkrem state to disk as the remote peer - // has just added to our local elkrem receiver, and given us a - // new pending revocation key. - if err := putChanElkremState(nodeChanBucket, c); err != nil { + // Persist the latest preimage state to disk as the remote peer + // has just added to our local preimage store, and + // given us a new pending revocation key. + if err := putChanPreimageState(nodeChanBucket, c); err != nil { return err } - // With the current elkrem state updated, append a new log - // entry recording this the delta of this state transition. + // With the current preimage producer/store state updated, + // append a new log entry recording this the delta of this state + // transition. // TODO(roasbeef): could make the deltas relative, would save // space, but then tradeoff for more disk-seeks to recover the // full state. @@ -737,7 +746,7 @@ func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := putChanFundingInfo(nodeChanBucket, channel); err != nil { return err } - if err := putChanElkremState(nodeChanBucket, channel); err != nil { + if err := putChanPreimageState(nodeChanBucket, channel); err != nil { return err } if err := putChanDeliveryScripts(nodeChanBucket, channel); err != nil { @@ -774,8 +783,8 @@ func fetchOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err = fetchChanFundingInfo(nodeChanBucket, channel); err != nil { return nil, fmt.Errorf("unable to read funding info: %v", err) } - if err = fetchChanElkremState(nodeChanBucket, channel); err != nil { - return nil, fmt.Errorf("uable to read elkrem state: %v", err) + if err = fetchChanPreimageState(nodeChanBucket, channel); err != nil { + return nil, err } if err = fetchChanDeliveryScripts(nodeChanBucket, channel); err != nil { return nil, fmt.Errorf("unable to read delivery scripts: %v", err) @@ -849,7 +858,7 @@ func deleteOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket, if err := deleteChanFundingInfo(nodeChanBucket, channelID); err != nil { return err } - if err := deleteChanElkremState(nodeChanBucket, channelID); err != nil { + if err := deleteChanPreimageState(nodeChanBucket, channelID); err != nil { return err } if err := deleteChanDeliveryScripts(nodeChanBucket, channelID); err != nil { @@ -1459,16 +1468,7 @@ func fetchChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel) err return nil } -func putChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { - var bc bytes.Buffer - if err := writeOutpoint(&bc, channel.ChanID); err != nil { - return err - } - - elkremKey := make([]byte, len(elkremStateKey)+bc.Len()) - copy(elkremKey[:3], elkremStateKey) - copy(elkremKey[3:], bc.Bytes()) - +func putChanPreimageState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer revKey := channel.TheirCurrentRevocation.SerializeCompressed() @@ -1482,16 +1482,19 @@ func putChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error // TODO(roasbeef): shouldn't be storing on disk, should re-derive as // needed - senderBytes := channel.LocalElkrem.ToBytes() - if err := wire.WriteVarBytes(&b, 0, senderBytes); err != nil { - return err - } - - reciverBytes, err := channel.RemoteElkrem.ToBytes() + data, err := channel.RevocationProducer.ToBytes() if err != nil { return err } - if err := wire.WriteVarBytes(&b, 0, reciverBytes); err != nil { + if err := wire.WriteVarBytes(&b, 0, data); err != nil { + return err + } + + data, err = channel.RevocationStore.ToBytes() + if err != nil { + return err + } + if err := wire.WriteVarBytes(&b, 0, data); err != nil { return err } @@ -1499,28 +1502,36 @@ func putChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error return err } - return nodeChanBucket.Put(elkremKey, b.Bytes()) + var bc bytes.Buffer + if err := writeOutpoint(&bc, channel.ChanID); err != nil { + return err + } + + preimageKey := make([]byte, len(preimageStateKey)+bc.Len()) + copy(preimageKey[:3], preimageStateKey) + copy(preimageKey[3:], bc.Bytes()) + return nodeChanBucket.Put(preimageKey, b.Bytes()) } -func deleteChanElkremState(nodeChanBucket *bolt.Bucket, chanID []byte) error { - elkremKey := make([]byte, len(elkremStateKey)+len(chanID)) - copy(elkremKey[:3], elkremStateKey) - copy(elkremKey[3:], chanID) - return nodeChanBucket.Delete(elkremKey) +func deleteChanPreimageState(nodeChanBucket *bolt.Bucket, chanID []byte) error { + preimageKey := make([]byte, len(preimageStateKey)+len(chanID)) + copy(preimageKey[:3], preimageStateKey) + copy(preimageKey[3:], chanID) + return nodeChanBucket.Delete(preimageKey) } -func fetchChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { +func fetchChanPreimageState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error { var b bytes.Buffer if err := writeOutpoint(&b, channel.ChanID); err != nil { return err } - elkremKey := make([]byte, len(elkremStateKey)+b.Len()) - copy(elkremKey[:3], elkremStateKey) - copy(elkremKey[3:], b.Bytes()) + preimageKey := make([]byte, len(preimageStateKey)+b.Len()) + copy(preimageKey[:3], preimageStateKey) + copy(preimageKey[3:], b.Bytes()) - elkremStateBytes := bytes.NewReader(nodeChanBucket.Get(elkremKey)) + reader := bytes.NewReader(nodeChanBucket.Get(preimageKey)) - revKeyBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") + revKeyBytes, err := wire.ReadVarBytes(reader, 0, 1000, "") if err != nil { return err } @@ -1529,32 +1540,30 @@ func fetchChanElkremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) err return err } - if _, err := elkremStateBytes.Read(channel.TheirCurrentRevocationHash[:]); err != nil { + if _, err := reader.Read(channel.TheirCurrentRevocationHash[:]); err != nil { return err } // TODO(roasbeef): should be rederiving on fly, or encrypting on disk. - senderBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") + producerBytes, err := wire.ReadVarBytes(reader, 0, 1000, "") if err != nil { return err } - elkremRoot, err := chainhash.NewHash(senderBytes) + channel.RevocationProducer, err = shachain.NewRevocationProducerFromBytes(producerBytes) if err != nil { return err } - channel.LocalElkrem = elkrem.NewElkremSender(*elkremRoot) - reciverBytes, err := wire.ReadVarBytes(elkremStateBytes, 0, 1000, "") + storeBytes, err := wire.ReadVarBytes(reader, 0, 1000, "") if err != nil { return err } - remoteE, err := elkrem.ElkremReceiverFromBytes(reciverBytes) + channel.RevocationStore, err = shachain.NewRevocationStoreFromBytes(storeBytes) if err != nil { return err } - channel.RemoteElkrem = remoteE - _, err = io.ReadFull(elkremStateBytes, channel.StateHintObsfucator[:]) + _, err = io.ReadFull(reader, channel.StateHintObsfucator[:]) if err != nil { return err } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 1baf1abb..166ca7e5 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/elkrem" + "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -117,17 +117,16 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) { return nil, err } - // Simulate 1000 channel updates via progression of the elkrem - // revocation trees. - sender := elkrem.NewElkremSender(key) - receiver := &elkrem.ElkremReceiver{} + // Simulate 1000 channel updates. + producer := shachain.NewRevocationProducer((*chainhash.Hash)(&key)) + store := shachain.NewRevocationStore() for i := 0; i < 1000; i++ { - preImage, err := sender.AtIndex(uint64(i)) + preImage, err := producer.AtIndex(uint64(i)) if err != nil { return nil, err } - if receiver.AddNext(preImage); err != nil { + if store.Store(preImage); err != nil { return nil, err } } @@ -150,8 +149,8 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) { TheirBalance: btcutil.Amount(9000), OurCommitTx: testTx, OurCommitSig: bytes.Repeat([]byte{1}, 71), - LocalElkrem: sender, - RemoteElkrem: receiver, + RevocationProducer: producer, + RevocationStore: store, StateHintObsfucator: obsfucator, FundingOutpoint: testOutpoint, OurMultiSigKey: privKey.PubKey(), @@ -207,34 +206,34 @@ func TestOpenChannelPutGetDelete(t *testing.T) { // The decoded channel state should be identical to what we stored // above. if !state.IdentityPub.IsEqual(newState.IdentityPub) { - t.Fatalf("their id doesn't match") + t.Fatal("their id doesn't match") } if !reflect.DeepEqual(state.ChanID, newState.ChanID) { - t.Fatalf("chan id's don't match") + t.Fatal("chan id's don't match") } if state.MinFeePerKb != newState.MinFeePerKb { - t.Fatalf("fee/kb doesn't match") + t.Fatal("fee/kb doesn't match") } if state.TheirDustLimit != newState.TheirDustLimit { - t.Fatalf("their dust limit doesn't match") + t.Fatal("their dust limit doesn't match") } if state.OurDustLimit != newState.OurDustLimit { - t.Fatalf("our dust limit doesn't match") + t.Fatal("our dust limit doesn't match") } if state.IsInitiator != newState.IsInitiator { - t.Fatalf("initiator status doesn't match") + t.Fatal("initiator status doesn't match") } if state.ChanType != newState.ChanType { - t.Fatalf("channel type doesn't match") + t.Fatal("channel type doesn't match") } if !bytes.Equal(state.OurCommitKey.SerializeCompressed(), newState.OurCommitKey.SerializeCompressed()) { - t.Fatalf("our commit key doesn't match") + t.Fatal("our commit key doesn't match") } if !bytes.Equal(state.TheirCommitKey.SerializeCompressed(), newState.TheirCommitKey.SerializeCompressed()) { - t.Fatalf("their commit key doesn't match") + t.Fatal("their commit key doesn't match") } if state.Capacity != newState.Capacity { @@ -242,49 +241,49 @@ func TestOpenChannelPutGetDelete(t *testing.T) { newState.Capacity) } if state.OurBalance != newState.OurBalance { - t.Fatalf("our balance doesn't match") + t.Fatal("our balance doesn't match") } if state.TheirBalance != newState.TheirBalance { - t.Fatalf("their balance doesn't match") + t.Fatal("their balance doesn't match") } var b1, b2 bytes.Buffer if err := state.OurCommitTx.Serialize(&b1); err != nil { - t.Fatalf("unable to serialize transaction") + t.Fatal("unable to serialize transaction") } if err := newState.OurCommitTx.Serialize(&b2); err != nil { - t.Fatalf("unable to serialize transaction") + t.Fatal("unable to serialize transaction") } if !bytes.Equal(b1.Bytes(), b2.Bytes()) { - t.Fatalf("ourCommitTx doesn't match") + t.Fatal("ourCommitTx doesn't match") } if !bytes.Equal(newState.OurCommitSig, state.OurCommitSig) { - t.Fatalf("commit sigs don't match") + t.Fatal("commit sigs don't match") } // TODO(roasbeef): replace with a single equal? if !reflect.DeepEqual(state.FundingOutpoint, newState.FundingOutpoint) { - t.Fatalf("funding outpoint doesn't match") + t.Fatal("funding outpoint doesn't match") } if !bytes.Equal(state.OurMultiSigKey.SerializeCompressed(), newState.OurMultiSigKey.SerializeCompressed()) { - t.Fatalf("our multisig key doesn't match") + t.Fatal("our multisig key doesn't match") } if !bytes.Equal(state.TheirMultiSigKey.SerializeCompressed(), newState.TheirMultiSigKey.SerializeCompressed()) { - t.Fatalf("their multisig key doesn't match") + t.Fatal("their multisig key doesn't match") } if !bytes.Equal(state.FundingWitnessScript, newState.FundingWitnessScript) { - t.Fatalf("redeem script doesn't match") + t.Fatal("redeem script doesn't match") } // The local and remote delivery scripts should be identical. if !bytes.Equal(state.OurDeliveryScript, newState.OurDeliveryScript) { - t.Fatalf("our delivery address doesn't match") + t.Fatal("our delivery address doesn't match") } if !bytes.Equal(state.TheirDeliveryScript, newState.TheirDeliveryScript) { - t.Fatalf("their delivery address doesn't match") + t.Fatal("their delivery address doesn't match") } if state.NumUpdates != newState.NumUpdates { @@ -304,33 +303,45 @@ func TestOpenChannelPutGetDelete(t *testing.T) { state.TotalSatoshisSent, newState.TotalSatoshisSent) } if state.TotalSatoshisReceived != newState.TotalSatoshisReceived { - t.Fatalf("satoshis received doesn't match") + t.Fatal("satoshis received doesn't match") } if state.CreationTime.Unix() != newState.CreationTime.Unix() { - t.Fatalf("creation time doesn't match") + t.Fatal("creation time doesn't match") } - // The local and remote elkrems should be identical. - if !bytes.Equal(state.LocalElkrem.ToBytes(), newState.LocalElkrem.ToBytes()) { - t.Fatalf("local elkrems don't match") - } - oldRemoteElkrem, err := state.RemoteElkrem.ToBytes() + // The local and remote producers should be identical. + oldProducer, err := state.RevocationProducer.ToBytes() if err != nil { - t.Fatalf("unable to serialize old remote elkrem: %v", err) + t.Fatalf("can't convert old revocation producer to bytes: %v", + err) } - newRemoteElkrem, err := newState.RemoteElkrem.ToBytes() + + newProducer, err := newState.RevocationProducer.ToBytes() if err != nil { - t.Fatalf("unable to serialize new remote elkrem: %v", err) + t.Fatalf("can't convert new revocation producer to bytes: %v", + err) } - if !bytes.Equal(oldRemoteElkrem, newRemoteElkrem) { - t.Fatalf("remote elkrems don't match") + + if !bytes.Equal(oldProducer, newProducer) { + t.Fatal("local producer don't match") + } + oldStore, err := state.RevocationStore.ToBytes() + if err != nil { + t.Fatalf("unable to serialize old remote store: %v", err) + } + newStore, err := newState.RevocationStore.ToBytes() + if err != nil { + t.Fatalf("unable to serialize new remote store: %v", err) + } + if !bytes.Equal(oldStore, newStore) { + t.Fatal("remote store don't match") } if !newState.TheirCurrentRevocation.IsEqual(state.TheirCurrentRevocation) { - t.Fatalf("revocation keys don't match") + t.Fatal("revocation keys don't match") } if !bytes.Equal(newState.TheirCurrentRevocationHash[:], state.TheirCurrentRevocationHash[:]) { - t.Fatalf("revocation hashes don't match") + t.Fatal("revocation hashes don't match") } if !reflect.DeepEqual(state.Htlcs[0], newState.Htlcs[0]) { t.Fatalf("htlcs don't match: %v vs %v", spew.Sdump(state.Htlcs[0]), @@ -338,7 +349,7 @@ func TestOpenChannelPutGetDelete(t *testing.T) { } if !bytes.Equal(state.StateHintObsfucator[:], newState.StateHintObsfucator[:]) { - t.Fatalf("obsfuctators don't match") + t.Fatal("obsfuctators don't match") } // Finally to wrap up the test, delete the state of the channel within @@ -363,7 +374,7 @@ func TestOpenChannelPutGetDelete(t *testing.T) { // should yield no results. openChans, err = cdb.FetchAllChannels() if err != nil { - t.Fatalf("unable to fetch all open chans") + t.Fatal("unable to fetch all open chans") } if len(openChans) != 0 { t.Fatalf("all channels not deleted, found %v", len(openChans)) @@ -494,13 +505,13 @@ func TestChannelStateTransition(t *testing.T) { // The two deltas (the original vs the on-disk version) should // identical, and all HTLC data should properly be retained. if delta.LocalBalance != diskDelta.LocalBalance { - t.Fatalf("local balances don't match") + t.Fatal("local balances don't match") } if delta.RemoteBalance != diskDelta.RemoteBalance { - t.Fatalf("remote balances don't match") + t.Fatal("remote balances don't match") } if delta.UpdateNum != diskDelta.UpdateNum { - t.Fatalf("update number doesn't match") + t.Fatal("update number doesn't match") } for i := 0; i < len(delta.Htlcs); i++ { originalHTLC := delta.Htlcs[i] @@ -546,7 +557,7 @@ func TestChannelStateTransition(t *testing.T) { } if !bytes.Equal(updatedChannel[0].TheirCurrentRevocationHash[:], newRevocation) { - t.Fatalf("revocation state wasn't synced!") + t.Fatal("revocation state wasn't synced!") } // Now attempt to delete the channel from the database. @@ -569,6 +580,6 @@ func TestChannelStateTransition(t *testing.T) { // revocation log has been deleted. _, err = updatedChannel[0].FindPreviousState(uint64(delta.UpdateNum)) if err == nil { - t.Fatalf("revocation log search should've failed") + t.Fatal("revocation log search should've failed") } } diff --git a/elkrem/elkrem.go b/elkrem/elkrem.go deleted file mode 100644 index 53b20da7..00000000 --- a/elkrem/elkrem.go +++ /dev/null @@ -1,151 +0,0 @@ -package elkrem - -import ( - "fmt" - - "github.com/roasbeef/btcd/chaincfg/chainhash" -) - -/* elkrem is a simpler alternative to the 64 dimensional sha-chain. -it's basically a reverse merkle tree. If we want to provide 2**64 possible -hashes, this requires a worst case computation of 63 hashes for the -sender, and worst-case storage of 64 hashes for the receiver. - -The operations are left hash L() and right hash R(), which are -hash(parent) and hash(parent, 1) respectively. (concatenate one byte) - -Here is a shorter example of a tree with 8 leaves and 15 total nodes. - -The sender first computes the bottom left leaf 0b0000. This is -L(L(L(L(root)))). The receiver stores leaf 0. - -Next the sender computes 0b0001. R(L(L(L(root)))). Receiver stores. -Next sender computes 0b1000 (8). L(L(L(root))). Receiver stores this, and -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 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 (i'th node) - sha *chainhash.Hash // hash -} -type ElkremSender struct { - root *chainhash.Hash // root hash of the tree -} -type ElkremReceiver struct { - s []ElkremNode // store of received hashes -} - -func LeftSha(in chainhash.Hash) chainhash.Hash { - return chainhash.DoubleHashH(in[:]) // left is sha(sha(in)) -} -func RightSha(in chainhash.Hash) chainhash.Hash { - return chainhash.DoubleHashH(append(in[:], 0x01)) // sha(sha(in, 1)) -} - -// iterative descent of sub-tree. w = hash number you want. i = input index -// h = height of input index. sha = input hash -func descend(w, i uint64, h uint8, sha chainhash.Hash) (chainhash.Hash, error) { - for w < i { - if w <= i-(1<= 0 { // if this is not the first hash (>= because we -1'd) - n.i = e.s[t].i + 1 // incoming index is tip of stack index + 1 - } - if t > 0 && e.s[t-1].h == e.s[t].h { // top 2 elements are equal height - // next node must be parent; verify and remove children - n.h = e.s[t].h + 1 // assign height - l := LeftSha(*sha) // calc l child - r := RightSha(*sha) // calc r child - if !e.s[t-1].sha.IsEqual(&l) { // test l child - return fmt.Errorf("left child doesn't match, expect %s got %s", - e.s[t-1].sha.String(), l.String()) - } - if !e.s[t].sha.IsEqual(&r) { // test r child - return fmt.Errorf("right child doesn't match, expect %s got %s", - e.s[t].sha.String(), r.String()) - } - e.s = e.s[:len(e.s)-2] // l and r children OK, remove them - } // if that didn't happen, height defaults to 0 - 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) (*chainhash.Hash, 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 - out = n - break - } - } - if out.sha == nil { // didn't find anything - return nil, fmt.Errorf("receiver has max %d, less than requested %d", - e.s[len(e.s)-1].i, w) - } - 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 deleted file mode 100644 index ca6f522c..00000000 --- a/elkrem/elkrem_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package elkrem - -import ( - "testing" - - "github.com/roasbeef/btcd/chaincfg/chainhash" -) - -// TestElkremBig tries 10K hashes -func TestElkremBig(t *testing.T) { - var rcv ElkremReceiver - - sndr := NewElkremSender(chainhash.DoubleHashH([]byte("elktest"))) - - for n := uint64(0); n < 10000; n++ { - sha, err := sndr.AtIndex(n) - if err != nil { - t.Fatal(err) - } - - if err = rcv.AddNext(sha); err != nil { - t.Fatal(err) - } - } - - ReceiverSerdesTest(t, &rcv) - - for n := uint64(0); n < 10000; n += 500 { - if _, err := rcv.AtIndex(n); err != nil { - t.Fatal(err) - } - } -} - -// TestElkremLess tries 10K hashes -func TestElkremLess(t *testing.T) { - var rcv ElkremReceiver - - sndr := NewElkremSender(chainhash.DoubleHashH([]byte("elktest2"))) - - for n := uint64(0); n < 5000; n++ { - sha, err := sndr.AtIndex(n) - if err != nil { - t.Fatal(err) - } - - if err = rcv.AddNext(sha); err != nil { - t.Fatal(err) - } - } - - for n := uint64(0); n < 5000; n += 500 { - if _, err := rcv.AtIndex(n); err != nil { - t.Fatal(err) - } - } -} diff --git a/elkrem/serdes.go b/elkrem/serdes.go deleted file mode 100644 index 4db43905..00000000 --- a/elkrem/serdes.go +++ /dev/null @@ -1,130 +0,0 @@ -package elkrem - -import ( - "bytes" - "encoding/binary" - "fmt" - - "github.com/roasbeef/btcd/chaincfg/chainhash" -) - -/* 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 48. Receivers are -prepended with the total number of hashes, so the total max size is 1969 bytes. -*/ - -// ToBytes turns the Elkrem Receiver into a bunch of bytes in a slice. -// 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, nil - } - 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 number of nodes (1 byte) - err := binary.Write(&buf, binary.BigEndian, numOfNodes) - if err != nil { - return nil, err - } - for _, node := range e.s { - // write 1 byte height - err = binary.Write(&buf, binary.BigEndian, node.h) - if err != nil { - return nil, err - } - // write 8 byte index - err = binary.Write(&buf, binary.BigEndian, node.i) - 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[:]) - if err != nil { - return nil, err - } - if n != 32 { // make sure that was 32 bytes - return nil, fmt.Errorf("%d byte hash, expect 32", n) - } - } - if buf.Len() != (int(numOfNodes)*41)+1 { - return nil, fmt.Errorf("Somehow made wrong size buf, got %d expect %d", - buf.Len(), (numOfNodes*41)+1) - } - return buf.Bytes(), nil -} - -func ElkremReceiverFromBytes(b []byte) (*ElkremReceiver, error) { - var e ElkremReceiver - if len(b) == 0 { // empty receiver, which is OK - return &e, nil - } - buf := bytes.NewBuffer(b) - // read 1 byte number of nodes stored in receiver - numOfNodes, err := buf.ReadByte() - if err != nil { - return nil, err - } - if numOfNodes < 1 || numOfNodes > maxHeight+1 { - return nil, fmt.Errorf("Read invalid number of nodes: %d", numOfNodes) - } - if buf.Len() != (int(numOfNodes) * 41) { - return nil, fmt.Errorf("Remaining buf wrong size, expect %d got %d", - (numOfNodes * 41), buf.Len()) - } - - e.s = make([]ElkremNode, numOfNodes) - - for j, _ := range e.s { - e.s[j].sha = new(chainhash.Hash) - // read 1 byte height - err := binary.Read(buf, binary.BigEndian, &e.s[j].h) - if err != nil { - return nil, err - } - // read 8 byte index - err = binary.Read(buf, binary.BigEndian, &e.s[j].i) - if err != nil { - return nil, err - } - // read 32 byte sha hash - err = e.s[j].sha.SetBytes(buf.Next(32)) - if err != nil { - return nil, err - } - // sanity check. Note that this doesn't check that index and height - // match. Could add that but it's slow. - 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 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) - } - - 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 -} - -// ToBytes returns the root of the elkrem sender tree as a byte slice. This -// function is in place to allow one to export the root of the tree. However, -// node that if one uses a deterministic procedure to generate the root, then -// serialization isn't necessary as it can simply be re-derived on the fly. -func (e *ElkremSender) ToBytes() []byte { - return e.root[:] -} diff --git a/elkrem/serdes_test.go b/elkrem/serdes_test.go deleted file mode 100644 index 4bd8e3a5..00000000 --- a/elkrem/serdes_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package elkrem - -import ( - "bytes" - "testing" -) - -func ReceiverSerdesTest(t *testing.T, rcv *ElkremReceiver) { - b, err := rcv.ToBytes() - if err != nil { - t.Fatal(err) - } - - rcv2, err := ElkremReceiverFromBytes(b) - if err != nil { - t.Fatal(err) - } - - b2, err := rcv2.ToBytes() - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(b, b2) { - t.Fatalf("First and second serializations different") - } -} - -//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) -// } - -// b2, err := sndr.ToBytes() -// if err != nil { -// t.Fatal(err) -// } - -// if !bytes.Equal(b, b2) { -// t.Fatalf("First and second serializations different") -// } -//} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index dfeb5066..21b6a41b 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -734,10 +734,10 @@ func newBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, return nil, err } - // With the state number broadcast known, we can now derive the proper - // leaf from our revocation tree necessary to sweep the remote party's + // With the state number broadcast known, we can now derive/restore the + // proper revocation preimage necessary to sweep the remote party's // output. - revocationPreimage, err := chanState.RemoteElkrem.AtIndex(stateNum) + revocationPreimage, err := chanState.RevocationStore.LookUp(stateNum) if err != nil { return nil, err } @@ -1482,7 +1482,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(rawSig []byte) error { // derive the key+hash needed to construct the new commitment view and // state. nextHeight := lc.currentHeight + 1 - revocation, err := lc.channelState.LocalElkrem.AtIndex(nextHeight) + revocation, err := lc.channelState.RevocationProducer.AtIndex(nextHeight) if err != nil { return err } @@ -1577,7 +1577,7 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, err // Now that we've accept a new state transition, we send the remote // party the revocation for our current commitment state. revocationMsg := &lnwire.RevokeAndAck{} - currentRevocation, err := lc.channelState.LocalElkrem.AtIndex(lc.currentHeight) + currentRevocation, err := lc.channelState.RevocationProducer.AtIndex(lc.currentHeight) if err != nil { return nil, err } @@ -1586,7 +1586,7 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, err // Along with this revocation, we'll also send an additional extension // to our revocation window to the remote party. lc.revocationWindowEdge++ - revocationEdge, err := lc.channelState.LocalElkrem.AtIndex(lc.revocationWindowEdge) + revocationEdge, err := lc.channelState.RevocationProducer.AtIndex(lc.revocationWindowEdge) if err != nil { return nil, err } @@ -1655,11 +1655,10 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ([]*P currentRevocationKey := lc.channelState.TheirCurrentRevocation pendingRevocation := chainhash.Hash(revMsg.Revocation) - // Ensure the new preimage fits in properly within the elkrem receiver - // tree. If this fails, then all other checks are skipped. + // Ensure that the new pre-image can be placed in preimage store. // TODO(rosbeef): abstract into func - remoteElkrem := lc.channelState.RemoteElkrem - if err := remoteElkrem.AddNext(&pendingRevocation); err != nil { + store := lc.channelState.RevocationStore + if err := store.Store(&pendingRevocation); err != nil { return nil, err } @@ -1698,8 +1697,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ([]*P // At this point, the revocation has been accepted, and we've rotated // the current revocation key+hash for the remote party. Therefore we - // sync now to ensure the elkrem receiver state is consistent with the - // current commitment height. + // sync now to ensure the revocation producer state is consistent with + // the current commitment height. tail := lc.remoteCommitChain.tail() delta, err := tail.toChannelDelta() if err != nil { @@ -1778,7 +1777,7 @@ func (lc *LightningChannel) ExtendRevocationWindow() (*lnwire.RevokeAndAck, erro revMsg.ChannelPoint = *lc.channelState.ChanID nextHeight := lc.revocationWindowEdge + 1 - revocation, err := lc.channelState.LocalElkrem.AtIndex(nextHeight) + revocation, err := lc.channelState.RevocationProducer.AtIndex(nextHeight) if err != nil { return nil, err } @@ -2147,8 +2146,8 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) { // Re-derive the original pkScript for out to-self output within the // commitment transaction. We'll need this for the created sign // descriptor. - elkrem := lc.channelState.LocalElkrem - unusedRevocation, err := elkrem.AtIndex(lc.currentHeight) + producer := lc.channelState.RevocationProducer + unusedRevocation, err := producer.AtIndex(lc.currentHeight) if err != nil { return nil, err } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 82f546c3..eca2b7c7 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -11,8 +11,8 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/elkrem" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -202,15 +202,17 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan } fundingTxIn := wire.NewTxIn(prevOut, nil, nil) - bobElkrem := elkrem.NewElkremSender(deriveElkremRoot(bobKeyPriv, bobKeyPub, aliceKeyPub)) - bobFirstRevoke, err := bobElkrem.AtIndex(0) + bobRoot := deriveRevocationRoot(bobKeyPriv, bobKeyPub, aliceKeyPub) + bobPreimageProducer := shachain.NewRevocationProducer(bobRoot) + bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err } bobRevokeKey := DeriveRevocationPubkey(aliceKeyPub, bobFirstRevoke[:]) - aliceElkrem := elkrem.NewElkremSender(deriveElkremRoot(aliceKeyPriv, aliceKeyPub, bobKeyPub)) - aliceFirstRevoke, err := aliceElkrem.AtIndex(0) + aliceRoot := deriveRevocationRoot(aliceKeyPriv, aliceKeyPub, bobKeyPub) + alicePreimageProducer := shachain.NewRevocationProducer(aliceRoot) + aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { return nil, nil, nil, err } @@ -262,8 +264,8 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan LocalCsvDelay: csvTimeoutAlice, RemoteCsvDelay: csvTimeoutBob, TheirCurrentRevocation: bobRevokeKey, - LocalElkrem: aliceElkrem, - RemoteElkrem: &elkrem.ElkremReceiver{}, + RevocationProducer: alicePreimageProducer, + RevocationStore: shachain.NewRevocationStore(), TheirDustLimit: bobDustLimit, OurDustLimit: aliceDustLimit, Db: dbAlice, @@ -288,8 +290,8 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan LocalCsvDelay: csvTimeoutBob, RemoteCsvDelay: csvTimeoutAlice, TheirCurrentRevocation: aliceRevokeKey, - LocalElkrem: bobElkrem, - RemoteElkrem: &elkrem.ElkremReceiver{}, + RevocationProducer: bobPreimageProducer, + RevocationStore: shachain.NewRevocationStore(), TheirDustLimit: aliceDustLimit, OurDustLimit: bobDustLimit, Db: dbBob, @@ -598,9 +600,10 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { // transaction with some degree of error corresponds to the actual size. func TestCheckCommitTxSize(t *testing.T) { checkSize := func(channel *LightningChannel, count int) { - // Due to variable size of the signatures (71-73) we may have - // an estimation error. - BaseCommitmentTxSizeEstimationError := 4 + // Due to variable size of the signatures (70-73) in + // witness script actual size of commitment transaction might + // be lower on 6 weight. + BaseCommitmentTxSizeEstimationError := 6 commitTx, err := channel.getSignedCommitTx() if err != nil { diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index a687ee0d..0bb14fd9 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -752,31 +752,31 @@ func DeriveRevocationPrivKey(commitPrivKey *btcec.PrivateKey, return privRevoke } -// deriveElkremRoot derives an elkrem root unique to a channel given the +// deriveRevocationRoot derives an root unique to a channel given the // private key for our public key in the 2-of-2 multi-sig, and the remote -// node's multi-sig public key. The root is derived using the HKDF[1][2] +// node's multi-sig public key. The seed is derived using the HKDF[1][2] // instantiated with sha-256. The secret data used is our multi-sig private // key, with the salt being the remote node's public key. // // [1]: https://eprint.iacr.org/2010/264.pdf // [2]: https://tools.ietf.org/html/rfc5869 -func deriveElkremRoot(elkremDerivationRoot *btcec.PrivateKey, +func deriveRevocationRoot(derivationRoot *btcec.PrivateKey, localMultiSigKey *btcec.PublicKey, - remoteMultiSigKey *btcec.PublicKey) chainhash.Hash { + remoteMultiSigKey *btcec.PublicKey) *chainhash.Hash { - secret := elkremDerivationRoot.Serialize() + secret := derivationRoot.Serialize() salt := localMultiSigKey.SerializeCompressed() info := remoteMultiSigKey.SerializeCompressed() - rootReader := hkdf.New(sha256.New, secret, salt, info) + seedReader := hkdf.New(sha256.New, secret, salt, info) // It's safe to ignore the error her as we know for sure that we won't // be draining the HKDF past its available entropy horizon. // TODO(roasbeef): revisit... - var elkremRoot chainhash.Hash - rootReader.Read(elkremRoot[:]) + var root chainhash.Hash + seedReader.Read(root[:]) - return elkremRoot + return &root } // SetStateNumHint encodes the current state number within the passed diff --git a/lnwallet/size.go b/lnwallet/size.go index edd12533..c7aa78d9 100644 --- a/lnwallet/size.go +++ b/lnwallet/size.go @@ -85,14 +85,14 @@ const ( // - Marker: 1 byte WitnessHeaderSize = 1 + 1 - // CommitmentTransaction: 125 bytes + // CommitmentTransaction: 125 43 * num-htlc-outputs bytes // - Version: 4 bytes // - WitnessHeader <---- part of the witness data // - CountTxIn: 1 byte - // - TxIn: + // - TxIn: 41 bytes // FundingInput // - CountTxOut: 1 byte - // - TxOut: + // - TxOut: 74 + 43 * num-htlc-outputs bytes // OutputPayingToThem, // OutputPayingToUs, // ....HTLCOutputs... diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index bc9bdcb6..b3f9fe36 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -11,10 +11,10 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/elkrem" "github.com/roasbeef/btcd/chaincfg" "github.com/roasbeef/btcutil/hdkeychain" + "github.com/lightningnetwork/lnd/shachain" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/txscript" "github.com/roasbeef/btcd/wire" @@ -27,9 +27,9 @@ const ( // outside word. msgBufferSize = 100 - // elkremRootIndex is the top level HD key index from which secrets - // used to generate elkrem roots should be derived from. - elkremRootIndex = hdkeychain.HardenedKeyStart + 1 + // revocationRootIndex is the top level HD key index from which secrets + // used to generate producer roots should be derived from. + revocationRootIndex = hdkeychain.HardenedKeyStart + 1 // identityKeyIndex is the top level HD key index which is used to // generate/rotate identity keys. @@ -42,7 +42,6 @@ const ( ) var ( - // Namespace bucket keys. lightningNamespaceKey = []byte("ln-wallet") waddrmgrNamespaceKey = []byte("waddrmgr") @@ -757,11 +756,11 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Initialize an empty sha-chain for them, tracking the current pending // revocation hash (we don't yet know the preimage so we can't add it // to the chain). - e := &elkrem.ElkremReceiver{} - pendingReservation.partialState.RemoteElkrem = e + s := shachain.NewRevocationStore() + pendingReservation.partialState.RevocationStore = s pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationKey - masterElkremRoot, err := l.deriveMasterElkremRoot() + masterElkremRoot, err := l.deriveMasterRevocationRoot() if err != nil { req.err <- err return @@ -769,12 +768,11 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Now that we have their commitment key, we can create the revocation // key for the first version of our commitment transaction. To do so, - // we'll first create our elkrem root, then grab the first pre-iamge - // from it. - elkremRoot := deriveElkremRoot(masterElkremRoot, ourKey, theirKey) - elkremSender := elkrem.NewElkremSender(elkremRoot) - pendingReservation.partialState.LocalElkrem = elkremSender - firstPreimage, err := elkremSender.AtIndex(0) + // we'll first create our root, then produce the first pre-image. + root := deriveRevocationRoot(masterElkremRoot, ourKey, theirKey) + producer := shachain.NewRevocationProducer(root) + pendingReservation.partialState.RevocationProducer = producer + firstPreimage, err := producer.AtIndex(0) if err != nil { req.err <- err return @@ -812,7 +810,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // TODO(roasbeef): define obsfucator scheme for dual funder var stateObsfucator [StateHintSize]byte if pendingReservation.partialState.IsInitiator { - stateObsfucator, err = deriveStateHintObsfucator(elkremSender) + stateObsfucator, err = deriveStateHintObfuscator(producer) if err != nil { req.err <- err return @@ -902,7 +900,7 @@ func (l *LightningWallet) handleSingleContribution(req *addSingleContributionMsg } pendingReservation.partialState.FundingWitnessScript = witnessScript - masterElkremRoot, err := l.deriveMasterElkremRoot() + masterElkremRoot, err := l.deriveMasterRevocationRoot() if err != nil { req.err <- err return @@ -910,22 +908,22 @@ func (l *LightningWallet) handleSingleContribution(req *addSingleContributionMsg // Now that we know their commitment key, we can create the revocation // key for our version of the initial commitment transaction. - elkremRoot := deriveElkremRoot(masterElkremRoot, ourKey, theirKey) - elkremSender := elkrem.NewElkremSender(elkremRoot) - firstPreimage, err := elkremSender.AtIndex(0) + root := deriveRevocationRoot(masterElkremRoot, ourKey, theirKey) + producer := shachain.NewRevocationProducer(root) + firstPreimage, err := producer.AtIndex(0) if err != nil { req.err <- err return } - pendingReservation.partialState.LocalElkrem = elkremSender + pendingReservation.partialState.RevocationProducer = producer theirCommitKey := theirContribution.CommitKey ourRevokeKey := DeriveRevocationPubkey(theirCommitKey, firstPreimage[:]) // Initialize an empty sha-chain for them, tracking the current pending // revocation hash (we don't yet know the preimage so we can't add it // to the chain). - remoteElkrem := &elkrem.ElkremReceiver{} - pendingReservation.partialState.RemoteElkrem = remoteElkrem + remotePreimageStore := shachain.NewRevocationStore() + pendingReservation.partialState.RevocationStore = remotePreimageStore // Record the counterpaty's remaining contributions to the channel, // converting their delivery address into a public key script. @@ -1379,11 +1377,11 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amoun return nil } -// deriveMasterElkremRoot derives the private key which serves as the master -// elkrem root. This master secret is used as the secret input to a HKDF to -// generate elkrem secrets based on random, but public data. -func (l *LightningWallet) deriveMasterElkremRoot() (*btcec.PrivateKey, error) { - masterElkremRoot, err := l.rootKey.Child(elkremRootIndex) +// deriveMasterRevocationRoot derives the private key which serves as the master +// producer root. This master secret is used as the secret input to a HKDF to +// generate revocation secrets based on random, but public data. +func (l *LightningWallet) deriveMasterRevocationRoot() (*btcec.PrivateKey, error) { + masterElkremRoot, err := l.rootKey.Child(revocationRootIndex) if err != nil { return nil, err } @@ -1391,34 +1389,34 @@ func (l *LightningWallet) deriveMasterElkremRoot() (*btcec.PrivateKey, error) { return masterElkremRoot.ECPrivKey() } -// deriveStateHintObsfucator derives the bytes to be used for obsfucatating the -// state hints from the elkerem root to be used for a new channel. The -// obsfucator is generated by performing an additional sha256 hash of the first -// child derived from the elkrem root. The leading 4 bytes are used for the -// obsfucator. -func deriveStateHintObsfucator(elkremRoot *elkrem.ElkremSender) ([StateHintSize]byte, error) { - var obsfucator [StateHintSize]byte +// deriveStateHintObfuscator derives the bytes to be used for obfuscating the +// state hints from the root to be used for a new channel. The +// obfuscator is generated by performing an additional sha256 hash of the first +// child derived from the revocation root. The leading 4 bytes are used for the +// obfuscator. +func deriveStateHintObfuscator(producer shachain.Producer) ([StateHintSize]byte, error) { + var obfuscator [StateHintSize]byte - firstChild, err := elkremRoot.AtIndex(0) + firstChild, err := producer.AtIndex(0) if err != nil { - return obsfucator, err + return obfuscator, err } grandChild := fastsha256.Sum256(firstChild[:]) - copy(obsfucator[:], grandChild[:]) + copy(obfuscator[:], grandChild[:]) - return obsfucator, nil + return obfuscator, nil } // initStateHints properly sets the obsfucated state hints on both commitment // transactions using the passed obsfucator. func initStateHints(commit1, commit2 *wire.MsgTx, - obsfucator [StateHintSize]byte) error { + obfuscator [StateHintSize]byte) error { - if err := SetStateNumHint(commit1, 0, obsfucator); err != nil { + if err := SetStateNumHint(commit1, 0, obfuscator); err != nil { return err } - if err := SetStateNumHint(commit2, 0, obsfucator); err != nil { + if err := SetStateNumHint(commit2, 0, obfuscator); err != nil { return err } @@ -1426,7 +1424,7 @@ func initStateHints(commit1, commit2 *wire.MsgTx, } // selectInputs selects a slice of inputs necessary to meet the specified -// selection amount. If input selection is unable to suceed to to insuffcient +// selection amount. If input selection is unable to succeed to to insufficient // funds, a non-nil error is returned. Additionally, the total amount of the // selected coins are returned in order for the caller to properly handle // change+fees.