channeldb: modify schema to multiple-channels-per-peer

This commit overhauls the current schema for storing active channels in
order to support tracking+updating multiple open channels for a
particular peer.

Channels are now uniquely identified by an output (txid:index) rather
than an arbitrary hash value. As a result, the funding transaction is
no longer stored, as only the txin is required to lookup the original
transaction, and to sign for new commitment states.

A new bucket, nested within the bucket for a node’s Lightning ID has
been created. This new bucket acts as an index to the active channels
for a particular peer by storing all the active channel points as keys
within the bucket. This bucket can then be scanned in a linear fashion,
or queried randomly in order to retrieve channel information.

The split between top-level, and channel-level keys remains the same.
The primary modification comes in using the channel ID (the funding
outpoint) as the key suffix for all top-level and channel-level keys.
This commit is contained in:
Olaoluwa Osuntokun 2016-06-20 21:39:50 -07:00
parent 43c84c2ce5
commit c2818a549b
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 337 additions and 106 deletions

View File

@ -2,17 +2,16 @@ package channeldb
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"sync"
"time"
"github.com/boltdb/bolt"
"github.com/lightningnetwork/lnd/elkrem"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcwallet/walletdb"
"github.com/lightningnetwork/lnd/elkrem"
"github.com/roasbeef/btcutil"
"github.com/roasbeef/btcwallet/walletdb"
)
var (
@ -25,6 +24,15 @@ var (
// to gather stats such as total satoshis received.
openChannelBucket = []byte("ocb")
// chanIDBucket is a thrid-level bucket stored within a node's ID bucket
// in the open channel bucket. The resolution path looks something like:
// ocb -> nodeID -> cib. This bucket contains a series of keys with no
// values, these keys are the channel ID's of all the active channels
// we currently have with a specified nodeID. This bucket acts as an
// additional indexing allowing random access and sequential scans over
// active channels.
chanIDBucket = []byte("cib")
// closedChannelBucket stores summarization information concerning
// previously open, but now closed channels.
closedChannelBucket = []byte("ccb")
@ -91,7 +99,7 @@ type OpenChannel struct {
TheirLNID [wire.HashSize]byte
// The ID of a channel is the txid of the funding transaction.
ChanID [wire.HashSize]byte
ChanID *wire.OutPoint
MinFeePerKb btcutil.Amount
// Our reserve. Assume symmetric reserve amounts. Only needed if the
@ -114,10 +122,11 @@ type OpenChannel struct {
TheirCommitTx *wire.MsgTx
OurCommitTx *wire.MsgTx
// The final funding transaction. Kept for wallet-related records.
FundingTx *wire.MsgTx
// The outpoint of the final funding transaction.
FundingOutpoint *wire.OutPoint
MultiSigKey *btcec.PrivateKey
OurMultiSigKey *btcec.PrivateKey
TheirMultiSigKey *btcec.PublicKey
FundingRedeemScript []byte
// In blocks
@ -155,6 +164,46 @@ type OpenChannel struct {
sync.RWMutex
}
// FullSync serializes, and writes to disk the *full* channel state, using
// both the active channel bucket to store the prefixed column fields, and the
// remote node's ID to store the remainder of the channel state.
//
// NOTE: This method requires an active EncryptorDecryptor to be registered in
// order to encrypt sensitive information.
func (c *OpenChannel) FullSync() error {
return c.Db.store.Update(func(tx *bolt.Tx) error {
// First fetch the top level bucket which stores all data related to
// current, active channels.
chanBucket, err := tx.CreateBucketIfNotExists(openChannelBucket)
if err != nil {
return err
}
// Within this top level bucket, fetch the bucket dedicated to storing
// open channel data specific to the remote node.
nodeChanBucket, err := chanBucket.CreateBucketIfNotExists(c.TheirLNID[:])
if err != nil {
return err
}
// Add this channel ID to the node's active channel index if
// it doesn't already exist.
chanIDBucket, err := nodeChanBucket.CreateBucketIfNotExists(chanIDBucket)
if err != nil {
return err
}
var b bytes.Buffer
if err := writeOutpoint(&b, c.ChanID); err != nil {
return err
}
if chanIDBucket.Get(b.Bytes()) == nil {
chanIDBucket.Put(b.Bytes(), nil)
}
return putOpenChannel(chanBucket, nodeChanBucket, c, c.Db.cryptoSystem)
})
}
// ChannelSnapshot....
// TODO(roasbeef): methods to roll forwards/backwards in state etc
// * use botldb cursor?
@ -195,29 +244,6 @@ func (c OpenChannel) RecordChannelDelta(theirRevokedCommit *wire.MsgTx, updateNu
return nil
}
// FullSync serializes, and writes to disk the *full* channel state, using
// both the active channel bucket to store the prefixed column fields, and the
// remote node's ID to store the remainder of the channel state.
//
// NOTE: This method requires an active EncryptorDecryptor to be registered in
// order to encrypt sensitive information.
func (c *OpenChannel) FullSync() error {
return c.Db.store.Update(func(tx *bolt.Tx) error {
// First fetch the top level bucket which stores all data related to
// current, active channels.
chanBucket := tx.Bucket(openChannelBucket)
// WIthin this top level bucket, fetch the bucket dedicated to storing
// open channel data specific to the remote node.
nodeChanBucket, err := chanBucket.CreateBucketIfNotExists(c.TheirLNID[:])
if err != nil {
return err
}
return putOpenChannel(chanBucket, nodeChanBucket, c, c.Db.cryptoSystem)
})
}
// putChannel serializes, and stores the current state of the channel in its
// entirety.
func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
@ -269,19 +295,13 @@ func putOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
// sensitive) the complete channel currently active with the passed nodeID.
// An EncryptorDecryptor is required to decrypt sensitive information stored
// within the database.
func fetchOpenChannel(openChanBucket *bolt.Bucket, nodeID [32]byte,
decryptor EncryptorDecryptor) (*OpenChannel, error) {
func fetchOpenChannel(openChanBucket *bolt.Bucket, nodeChanBucket *bolt.Bucket,
chanID *wire.OutPoint, decryptor EncryptorDecryptor) (*OpenChannel, error) {
// WIthin this top level bucket, fetch the bucket dedicated to storing
// open channel data specific to the remote node.
nodeChanBucket := openChanBucket.Bucket(nodeID[:])
if nodeChanBucket == nil {
return nil, fmt.Errorf("node chan bucket for node %v does not exist",
hex.EncodeToString(nodeID[:]))
channel := &OpenChannel{
ChanID: chanID,
}
channel := &OpenChannel{}
// First, read out the fields of the channel update less frequently.
if err := fetchChannelIDs(nodeChanBucket, channel); err != nil {
return nil, err
@ -330,8 +350,13 @@ func putChanCapacity(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
scratch2 := make([]byte, 8)
scratch3 := make([]byte, 8)
keyPrefix := make([]byte, 3+32)
copy(keyPrefix[3:], channel.ChanID[:])
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix[3:], b.Bytes())
copy(keyPrefix[:3], chanCapacityPrefix)
byteOrder.PutUint64(scratch1, uint64(channel.Capacity))
@ -351,9 +376,14 @@ func putChanCapacity(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
}
func fetchChanCapacity(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
// A byte slice re-used to compute each key prefix eblow.
keyPrefix := make([]byte, 3+32)
copy(keyPrefix[3:], channel.ChanID[:])
// A byte slice re-used to compute each key prefix below.
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix[3:], b.Bytes())
copy(keyPrefix[:3], chanCapacityPrefix)
capacityBytes := openChanBucket.Get(keyPrefix)
@ -374,17 +404,27 @@ func putChanMinFeePerKb(openChanBucket *bolt.Bucket, channel *OpenChannel) error
scratch := make([]byte, 8)
byteOrder.PutUint64(scratch, uint64(channel.MinFeePerKb))
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, minFeePerKbPrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
return openChanBucket.Put(keyPrefix, scratch)
}
func fetchChanMinFeePerKb(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, minFeePerKbPrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
feeBytes := openChanBucket.Get(keyPrefix)
channel.MinFeePerKb = btcutil.Amount(byteOrder.Uint64(feeBytes))
@ -396,17 +436,27 @@ func putChanNumUpdates(openChanBucket *bolt.Bucket, channel *OpenChannel) error
scratch := make([]byte, 8)
byteOrder.PutUint64(scratch, channel.NumUpdates)
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, updatePrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
return openChanBucket.Put(keyPrefix, scratch)
}
func fetchChanNumUpdates(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, updatePrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
updateBytes := openChanBucket.Get(keyPrefix)
channel.NumUpdates = byteOrder.Uint64(updateBytes)
@ -417,8 +467,14 @@ func fetchChanNumUpdates(openChanBucket *bolt.Bucket, channel *OpenChannel) erro
func putChanTotalFlow(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
scratch1 := make([]byte, 8)
scratch2 := make([]byte, 8)
keyPrefix := make([]byte, 3+32)
copy(keyPrefix[3:], channel.ChanID[:])
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix[3:], b.Bytes())
copy(keyPrefix[:3], satSentPrefix)
byteOrder.PutUint64(scratch1, uint64(channel.TotalSatoshisSent))
@ -432,8 +488,13 @@ func putChanTotalFlow(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
}
func fetchChanTotalFlow(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
keyPrefix := make([]byte, 3+32)
copy(keyPrefix[3:], channel.ChanID[:])
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix[3:], b.Bytes())
copy(keyPrefix[:3], satSentPrefix)
totalSentBytes := openChanBucket.Get(keyPrefix)
@ -448,19 +509,30 @@ func fetchChanTotalFlow(openChanBucket *bolt.Bucket, channel *OpenChannel) error
func putChanNetFee(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
scratch := make([]byte, 8)
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, netFeesPrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
byteOrder.PutUint64(scratch, uint64(channel.TotalNetFees))
return openChanBucket.Put(keyPrefix, scratch)
}
func fetchChanNetFee(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
keyPrefix := make([]byte, 3+32)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
keyPrefix := make([]byte, 3+b.Len())
copy(keyPrefix, netFeesPrefix)
copy(keyPrefix[3:], channel.ChanID[:])
copy(keyPrefix[3:], b.Bytes())
feeBytes := openChanBucket.Get(keyPrefix)
channel.TotalNetFees = byteOrder.Uint64(feeBytes)
@ -468,18 +540,33 @@ func fetchChanNetFee(openChanBucket *bolt.Bucket, channel *OpenChannel) error {
}
func putChannelIDs(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
idSlice := make([]byte, wire.HashSize*2)
copy(idSlice, channel.TheirLNID[:])
copy(idSlice[32:], channel.ChanID[:])
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
return nodeChanBucket.Put(chanIDKey, idSlice)
// Construct the id key: cid || channelID.
// TODO(roasbeef): abstract out to func
idKey := make([]byte, len(chanIDKey)+b.Len())
copy(idKey[:3], chanIDKey)
copy(idKey[3:], b.Bytes())
return nodeChanBucket.Put(idKey, channel.TheirLNID[:])
}
func fetchChannelIDs(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
idBytes := nodeChanBucket.Get(chanIDKey)
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
copy(channel.TheirLNID[:], idBytes[:32])
copy(channel.ChanID[:], idBytes[32:])
// Construct the id key: cid || channelID.
idKey := make([]byte, len(chanIDKey)+b.Len())
copy(idKey[:3], chanIDKey)
copy(idKey[3:], b.Bytes())
idBytes := nodeChanBucket.Get(idKey)
copy(channel.TheirLNID[:], idBytes)
return nil
}
@ -487,6 +574,16 @@ func fetchChannelIDs(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
func putChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
ed EncryptorDecryptor) error {
// Construct the key which stores the commitment keys: ckk || channelID.
// TODO(roasbeef): factor into func
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
commitKey := make([]byte, len(commitKeys)+bc.Len())
copy(commitKey[:3], commitKeys)
copy(commitKeys[3:], bc.Bytes())
var b bytes.Buffer
if _, err := b.Write(channel.TheirCommitKey.SerializeCompressed()); err != nil {
@ -502,14 +599,24 @@ func putChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
return err
}
return nodeChanBucket.Put(commitKeys, b.Bytes())
return nodeChanBucket.Put(commitKey, b.Bytes())
}
func fetchChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
ed EncryptorDecryptor) error {
// Construct the key which stores the commitment keys: ckk || channelID.
// TODO(roasbeef): factor into func
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
commitKey := make([]byte, len(commitKeys)+bc.Len())
copy(commitKey[:3], commitKeys)
copy(commitKeys[3:], bc.Bytes())
var err error
keyBytes := nodeChanBucket.Get(commitKeys)
keyBytes := nodeChanBucket.Get(commitKey)
channel.TheirCommitKey, err = btcec.ParsePubKey(keyBytes[:33], btcec.S256())
if err != nil {
@ -530,6 +637,14 @@ func fetchChanCommitKeys(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
}
func putChanCommitTxns(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
txnsKey := make([]byte, len(commitTxnsKey)+bc.Len())
copy(txnsKey[:3], commitTxnsKey)
copy(txnsKey[3:], bc.Bytes())
var b bytes.Buffer
if err := channel.TheirCommitTx.Serialize(&b); err != nil {
@ -550,11 +665,19 @@ func putChanCommitTxns(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error
return err
}
return nodeChanBucket.Put(commitTxnsKey, b.Bytes())
return nodeChanBucket.Put(txnsKey, b.Bytes())
}
func fetchChanCommitTxns(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
txnBytes := bytes.NewReader(nodeChanBucket.Get(commitTxnsKey))
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
txnsKey := make([]byte, len(commitTxnsKey)+bc.Len())
copy(txnsKey[:3], commitTxnsKey)
copy(txnsKey[3:], bc.Bytes())
txnBytes := bytes.NewReader(nodeChanBucket.Get(txnsKey))
channel.TheirCommitTx = wire.NewMsgTx()
if err := channel.TheirCommitTx.Deserialize(txnBytes); err != nil {
@ -583,19 +706,32 @@ func fetchChanCommitTxns(nodeChanBucket *bolt.Bucket, channel *OpenChannel) erro
func putChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
ed EncryptorDecryptor) error {
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
fundTxnKey := make([]byte, len(fundingTxnKey)+bc.Len())
copy(fundTxnKey[:3], fundingTxnKey)
copy(fundTxnKey[3:], bc.Bytes())
var b bytes.Buffer
if err := channel.FundingTx.Serialize(&b); err != nil {
if err := writeOutpoint(&b, channel.FundingOutpoint); err != nil {
return err
}
encryptedPriv, err := ed.Encrypt(channel.MultiSigKey.Serialize())
encryptedPriv, err := ed.Encrypt(channel.OurMultiSigKey.Serialize())
if err != nil {
return err
}
if err := wire.WriteVarBytes(&b, 0, encryptedPriv); err != nil {
return err
}
theirSerKey := channel.TheirMultiSigKey.SerializeCompressed()
if err := wire.WriteVarBytes(&b, 0, theirSerKey); err != nil {
return err
}
if err := wire.WriteVarBytes(&b, 0, channel.FundingRedeemScript[:]); err != nil {
return err
@ -608,16 +744,25 @@ func putChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
return err
}
return nodeChanBucket.Put(fundingTxnKey, b.Bytes())
return nodeChanBucket.Put(fundTxnKey, b.Bytes())
}
func fetchChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
ed EncryptorDecryptor) error {
infoBytes := bytes.NewReader(nodeChanBucket.Get(fundingTxnKey))
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
fundTxnKey := make([]byte, len(fundingTxnKey)+b.Len())
copy(fundTxnKey[:3], fundingTxnKey)
copy(fundTxnKey[3:], b.Bytes())
channel.FundingTx = wire.NewMsgTx()
if err := channel.FundingTx.Deserialize(infoBytes); err != nil {
infoBytes := bytes.NewReader(nodeChanBucket.Get(fundTxnKey))
// TODO(roasbeef): can remove as channel ID *is* the funding point now.
channel.FundingOutpoint = &wire.OutPoint{}
if err := readOutpoint(infoBytes, channel.FundingOutpoint); err != nil {
return err
}
@ -629,7 +774,16 @@ func fetchChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
if err != nil {
return err
}
channel.MultiSigKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), decryptedPriv)
channel.OurMultiSigKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), decryptedPriv)
theirKeyBytes, err := wire.ReadVarBytes(infoBytes, 0, 33, "")
if err != nil {
return err
}
channel.TheirMultiSigKey, err = btcec.ParsePubKey(theirKeyBytes, btcec.S256())
if err != nil {
return err
}
channel.FundingRedeemScript, err = wire.ReadVarBytes(infoBytes, 0, 520, "")
if err != nil {
@ -647,6 +801,15 @@ func fetchChanFundingInfo(nodeChanBucket *bolt.Bucket, channel *OpenChannel,
}
func putChanEklremState(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())
var b bytes.Buffer
if _, err := b.Write(channel.TheirCurrentRevocation[:]); err != nil {
@ -669,11 +832,19 @@ func putChanEklremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error
return err
}
return nodeChanBucket.Put(elkremStateKey, b.Bytes())
return nodeChanBucket.Put(elkremKey, b.Bytes())
}
func fetchChanEklremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
elkremStateBytes := bytes.NewReader(nodeChanBucket.Get(elkremStateKey))
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())
elkremStateBytes := bytes.NewReader(nodeChanBucket.Get(elkremKey))
if _, err := elkremStateBytes.Read(channel.TheirCurrentRevocation[:]); err != nil {
return err
@ -703,6 +874,14 @@ func fetchChanEklremState(nodeChanBucket *bolt.Bucket, channel *OpenChannel) err
}
func putChanDeliveryScripts(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
var bc bytes.Buffer
if err := writeOutpoint(&bc, channel.ChanID); err != nil {
return err
}
deliveryKey := make([]byte, len(deliveryScriptsKey)+bc.Len())
copy(deliveryKey[:3], deliveryScriptsKey)
copy(deliveryKey[3:], bc.Bytes())
var b bytes.Buffer
if err := wire.WriteVarBytes(&b, 0, channel.OurDeliveryScript); err != nil {
return err
@ -716,6 +895,14 @@ func putChanDeliveryScripts(nodeChanBucket *bolt.Bucket, channel *OpenChannel) e
}
func fetchChanDeliveryScripts(nodeChanBucket *bolt.Bucket, channel *OpenChannel) error {
var b bytes.Buffer
if err := writeOutpoint(&b, channel.ChanID); err != nil {
return err
}
deliveryKey := make([]byte, len(deliveryScriptsKey)+b.Len())
copy(deliveryKey[:3], deliveryScriptsKey)
copy(deliveryKey[3:], b.Bytes())
var err error
deliveryBytes := bytes.NewReader(nodeChanBucket.Get(deliveryScriptsKey))
@ -731,3 +918,35 @@ func fetchChanDeliveryScripts(nodeChanBucket *bolt.Bucket, channel *OpenChannel)
return nil
}
func writeOutpoint(w io.Writer, o *wire.OutPoint) error {
scratch := make([]byte, 4)
if err := wire.WriteVarBytes(w, 0, o.Hash[:]); err != nil {
return err
}
byteOrder.PutUint32(scratch, o.Index)
if _, err := w.Write(scratch); err != nil {
return err
}
return nil
}
func readOutpoint(r io.Reader, o *wire.OutPoint) error {
scratch := make([]byte, 4)
txid, err := wire.ReadVarBytes(r, 0, 32, "prevout")
if err != nil {
return err
}
copy(o.Hash[:], txid)
if _, err := r.Read(scratch); err != nil {
return err
}
o.Index = byteOrder.Uint32(scratch)
return nil
}

View File

@ -4,16 +4,17 @@ import (
"bytes"
"io/ioutil"
"os"
"reflect"
"testing"
"time"
"github.com/lightningnetwork/lnd/elkrem"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire"
_ "github.com/roasbeef/btcwallet/walletdb/bdb"
"github.com/lightningnetwork/lnd/elkrem"
"github.com/roasbeef/btcutil"
_ "github.com/roasbeef/btcwallet/walletdb/bdb"
)
var (
@ -25,11 +26,14 @@ var (
0xd, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1e, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9,
}
id = [wire.HashSize]byte{
0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
0x2d, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1f, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9,
id = &wire.OutPoint{
Hash: [wire.HashSize]byte{
0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
0x48, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
0x2d, 0xe7, 0x93, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1f, 0xb, 0x4c, 0xf9, 0x9e, 0xc5, 0x8c, 0xe9,
},
Index: 9,
}
rev = [20]byte{
0x51, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
@ -68,6 +72,10 @@ var (
},
LockTime: 5,
}
testOutpoint = &wire.OutPoint{
Hash: key,
Index: 0,
}
)
type MockEncryptorDecryptor struct {
@ -132,7 +140,7 @@ func TestOpenChannelEncodeDecode(t *testing.T) {
}
state := OpenChannel{
TheirLNID: id,
TheirLNID: key,
ChanID: id,
MinFeePerKb: btcutil.Amount(5000),
OurCommitKey: privKey,
@ -144,8 +152,9 @@ func TestOpenChannelEncodeDecode(t *testing.T) {
OurCommitTx: testTx,
LocalElkrem: &sender,
RemoteElkrem: &receiver,
FundingTx: testTx,
MultiSigKey: privKey,
FundingOutpoint: testOutpoint,
OurMultiSigKey: privKey,
TheirMultiSigKey: privKey.PubKey(),
FundingRedeemScript: script,
TheirCurrentRevocation: rev,
OurDeliveryScript: script,
@ -164,17 +173,20 @@ func TestOpenChannelEncodeDecode(t *testing.T) {
t.Fatalf("unable to save and serialize channel state: %v", err)
}
newState, err := cdb.FetchOpenChannel(id)
nodeID := wire.ShaHash(state.TheirLNID)
openChannels, err := cdb.FetchOpenChannels(&nodeID)
if err != nil {
t.Fatalf("unable to fetch open channel: %v", err)
}
newState := openChannels[0]
// The decoded channel state should be identical to what we stored
// above.
if !bytes.Equal(state.TheirLNID[:], newState.TheirLNID[:]) {
t.Fatalf("their id doesn't match")
}
if !bytes.Equal(state.ChanID[:], newState.ChanID[:]) {
if !reflect.DeepEqual(state.ChanID, newState.ChanID) {
t.Fatalf("chan id's don't match")
}
if state.MinFeePerKb != newState.MinFeePerKb {
@ -228,19 +240,18 @@ func TestOpenChannelEncodeDecode(t *testing.T) {
b1.Reset()
b2.Reset()
if err := state.FundingTx.Serialize(&b1); err != nil {
t.Fatalf("unable to serialize transaction")
}
if err := newState.FundingTx.Serialize(&b2); err != nil {
t.Fatalf("unable to serialize transaction")
}
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
t.Fatalf("funding tx doesn't match")
// TODO(roasbeef): replace with a single equal?
if !reflect.DeepEqual(state.FundingOutpoint, newState.FundingOutpoint) {
t.Fatalf("funding outpoint doesn't match")
}
if !bytes.Equal(state.MultiSigKey.Serialize(),
newState.MultiSigKey.Serialize()) {
t.Fatalf("multisig key doesn't match")
if !bytes.Equal(state.OurMultiSigKey.Serialize(),
newState.OurMultiSigKey.Serialize()) {
t.Fatalf("our multisig key doesn't match")
}
if !bytes.Equal(state.TheirMultiSigKey.SerializeCompressed(),
newState.TheirMultiSigKey.SerializeCompressed()) {
t.Fatalf("their multisig key doesn't match")
}
if !bytes.Equal(state.FundingRedeemScript, newState.FundingRedeemScript) {
t.Fatalf("redeem script doesn't match")

View File

@ -3,5 +3,6 @@ package channeldb
import "fmt"
var (
ErrNoExists = fmt.Errorf("channel db has not yet been created")
ErrNoExists = fmt.Errorf("channel db has not yet been created")
ErrNoActiveChannels = fmt.Errorf("no active channels exist")
)