lnwallet: update tests and workflow to new OpenChannel struct

This commit is contained in:
Olaoluwa Osuntokun 2016-03-24 00:01:35 -07:00
parent 983294c444
commit 082a8a34e3
2 changed files with 85 additions and 35 deletions

@ -8,10 +8,10 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/LightningNetwork/lnd/elkrem"
"github.com/lightningnetwork/lnd/chainntfs" "github.com/lightningnetwork/lnd/chainntfs"
"github.com/lightningnetwork/lnd/chainntfs/btcdnotify" "github.com/lightningnetwork/lnd/chainntfs/btcdnotify"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/shachain"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
@ -477,6 +477,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
reservation.partialState.TheirLNID = req.nodeID reservation.partialState.TheirLNID = req.nodeID
ourContribution := reservation.ourContribution ourContribution := reservation.ourContribution
ourContribution.CsvDelay = req.csvDelay ourContribution.CsvDelay = req.csvDelay
reservation.partialState.LocalCsvDelay = req.csvDelay
// We hold the coin select mutex while querying for outputs, and // We hold the coin select mutex while querying for outputs, and
// performing coin selection in order to avoid inadvertent double spends // performing coin selection in order to avoid inadvertent double spends
@ -602,21 +603,30 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
req.resp <- nil req.resp <- nil
return return
} }
reservation.partialState.OurDeliveryAddress = deliveryAddress deliveryScript, err := txscript.PayToAddrScript(deliveryAddress)
ourContribution.DeliveryAddress = deliveryAddress
// Create a new shaChain for verifiable transaction revocations. This
// will be used to generate revocation hashes for our past/current
// commitment transactions once we start to make payments within the
// channel.
shaChain, err := shachain.NewFromSeed(nil, 0)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.partialState.OurShaChain = shaChain reservation.partialState.OurDeliveryScript = deliveryScript
copy(ourContribution.RevocationHash[:], shaChain.CurrentRevocationHash()) ourContribution.DeliveryAddress = deliveryAddress
// Create a new elkrem for verifiable transaction revocations. This
// will be used to generate revocation hashes for our past/current
// commitment transactions once we start to make payments within the
// channel.
// TODO(roabeef): should be HMAC based...REMOVE BEFORE ALPHA
var zero wire.ShaHash
elkremSender := elkrem.NewElkremSender(63, zero)
reservation.partialState.LocalElkrem = &elkremSender
firstPrimage, err := elkremSender.AtIndex(0)
if err != nil {
req.err <- err
req.resp <- nil
return
}
copy(ourContribution.RevocationHash[:], btcutil.Hash160(firstPrimage[:]))
// Funding reservation request succesfully handled. The funding inputs // Funding reservation request succesfully handled. The funding inputs
// will be marked as unavailable until the reservation is either // will be marked as unavailable until the reservation is either
@ -793,7 +803,15 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// Initialize an empty sha-chain for them, tracking the current pending // Initialize an empty sha-chain for them, tracking the current pending
// revocation hash (we don't yet know the pre-image so we can't add it // revocation hash (we don't yet know the pre-image so we can't add it
// to the chain). // to the chain).
pendingReservation.partialState.TheirShaChain = shachain.New() e := elkrem.NewElkremReceiver(63)
// TODO(roasbeef): this is incorrect!! fix before lnstate integration
var zero wire.ShaHash
if err := e.AddNext(&zero); err != nil {
req.err <- nil
return
}
pendingReservation.partialState.RemoteElkrem = &e
pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationHash pendingReservation.partialState.TheirCurrentRevocation = theirContribution.RevocationHash
// Grab the hash of the current pre-image in our chain, this is needed // Grab the hash of the current pre-image in our chain, this is needed
@ -834,9 +852,15 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
txsort.InPlaceSort(ourCommitTx) txsort.InPlaceSort(ourCommitTx)
txsort.InPlaceSort(theirCommitTx) txsort.InPlaceSort(theirCommitTx)
deliveryScript, err := txscript.PayToAddrScript(theirContribution.DeliveryAddress)
if err != nil {
req.err <- err
return
}
// Record newly available information witin the open channel state. // Record newly available information witin the open channel state.
pendingReservation.partialState.CsvDelay = theirContribution.CsvDelay pendingReservation.partialState.RemoteCsvDelay = theirContribution.CsvDelay
pendingReservation.partialState.TheirDeliveryAddress = theirContribution.DeliveryAddress pendingReservation.partialState.TheirDeliveryScript = deliveryScript
pendingReservation.partialState.ChanID = fundingNTxid pendingReservation.partialState.ChanID = fundingNTxid
pendingReservation.partialState.TheirCommitKey = theirCommitKey pendingReservation.partialState.TheirCommitKey = theirCommitKey
pendingReservation.partialState.TheirCommitTx = theirCommitTx pendingReservation.partialState.TheirCommitTx = theirCommitTx
@ -979,14 +1003,16 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
// Add the complete funding transaction to the DB, in it's open bucket // Add the complete funding transaction to the DB, in it's open bucket
// which will be used for the lifetime of this channel. // which will be used for the lifetime of this channel.
err = l.channelDB.PutOpenChannel(pendingReservation.partialState) if err := pendingReservation.partialState.FullSync(); err != nil {
msg.err <- err
return
}
// Create a goroutine to watch the chain so we can open the channel once // Create a goroutine to watch the chain so we can open the channel once
// the funding tx has enough confirmations. // the funding tx has enough confirmations.
// TODO(roasbeef): add number of confs to the confi // TODO(roasbeef): add number of confs to the confi
go l.openChannelAfterConfirmations(pendingReservation, 3) go l.openChannelAfterConfirmations(pendingReservation, 3)
msg.err <- nil
msg.err <- err
} }
// openChannelAfterConfirmations creates, and opens a payment channel after // openChannelAfterConfirmations creates, and opens a payment channel after
@ -1033,3 +1059,19 @@ func (l *LightningWallet) getNextRawKey() (*btcec.PrivateKey, error) {
return pkAddr.PrivKey() return pkAddr.PrivKey()
} }
type waddrmgrEncryptorDecryptor struct {
m *waddrmgr.Manager
}
func (w *waddrmgrEncryptorDecryptor) Encrypt(p []byte) ([]byte, error) {
return w.m.Encrypt(waddrmgr.CKTPrivate, p)
}
func (w *waddrmgrEncryptorDecryptor) Decrypt(c []byte) ([]byte, error) {
return w.m.Decrypt(waddrmgr.CKTPrivate, c)
}
func (w *waddrmgrEncryptorDecryptor) OverheadSize() uint32 {
return 24
}

@ -5,10 +5,12 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/Roasbeef/btcd/rpctest" "github.com/Roasbeef/btcd/rpctest"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
@ -17,7 +19,6 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/coinset" "github.com/btcsuite/btcutil/coinset"
"github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
) )
var ( var (
@ -147,10 +148,8 @@ func newBobNode(miner *rpctest.Harness) (*bobNode, error) {
} }
// Give bobNode one 7 BTC output for use in creating channels. // Give bobNode one 7 BTC output for use in creating channels.
outputMap := map[string]btcutil.Amount{ output := &wire.TxOut{7e8, bobAddrScript}
bobAddr.String(): btcutil.Amount(7e8), mainTxid, err := miner.CoinbaseSpend([]*wire.TxOut{output})
}
mainTxid, err := miner.CoinbaseSpend(outputMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -175,7 +174,7 @@ func newBobNode(miner *rpctest.Harness) (*bobNode, error) {
prevOut := wire.NewOutPoint(mainTxid, index) prevOut := wire.NewOutPoint(mainTxid, index)
// TODO(roasbeef): When the chain rpc is hooked in, assert bob's output // TODO(roasbeef): When the chain rpc is hooked in, assert bob's output
// actually exists and it unspent in the chain. // actually exists and it unspent in the chain.
bobTxIn := wire.NewTxIn(prevOut, nil) bobTxIn := wire.NewTxIn(prevOut, nil, nil)
// Using bobs priv key above, create a change output he can spend. // Using bobs priv key above, create a change output he can spend.
bobChangeOutput := wire.NewTxOut(2*1e8, bobAddrScript) bobChangeOutput := wire.NewTxOut(2*1e8, bobAddrScript)
@ -205,7 +204,7 @@ func newBobNode(miner *rpctest.Harness) (*bobNode, error) {
func loadTestCredits(miner *rpctest.Harness, w *LightningWallet, numOutputs, btcPerOutput int) error { func loadTestCredits(miner *rpctest.Harness, w *LightningWallet, numOutputs, btcPerOutput int) error {
// Using the mining node, spend from a coinbase output numOutputs to // Using the mining node, spend from a coinbase output numOutputs to
// give us btcPerOutput with each output. // give us btcPerOutput with each output.
satoshiPerOutput := btcutil.Amount(btcPerOutput * 1e8) satoshiPerOutput := int64(btcPerOutput * 1e8)
addrs := make([]btcutil.Address, 0, numOutputs) addrs := make([]btcutil.Address, 0, numOutputs)
for i := 0; i < numOutputs; i++ { for i := 0; i < numOutputs; i++ {
// Grab a fresh address from the wallet to house this output. // Grab a fresh address from the wallet to house this output.
@ -214,10 +213,15 @@ func loadTestCredits(miner *rpctest.Harness, w *LightningWallet, numOutputs, btc
return err return err
} }
script, err := txscript.PayToAddrScript(walletAddr)
if err != nil {
return err
}
addrs = append(addrs, walletAddr) addrs = append(addrs, walletAddr)
outputMap := map[string]btcutil.Amount{walletAddr.String(): satoshiPerOutput} output := &wire.TxOut{satoshiPerOutput, script}
if _, err := miner.CoinbaseSpend(outputMap); err != nil { if _, err := miner.CoinbaseSpend([]*wire.TxOut{output}); err != nil {
return err return err
} }
} }
@ -279,7 +283,13 @@ func createTestWallet(miningNode *rpctest.Harness, netParams *chaincfg.Params) (
CACert: rpcConfig.Certificates, CACert: rpcConfig.Certificates,
} }
wallet, _, err := NewLightningWallet(config) dbDir := filepath.Join(tempTestDir, "cdb")
cdb, err := channeldb.Create(dbDir)
if err != nil {
return "", nil, err
}
wallet, err := NewLightningWallet(config, cdb)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -287,6 +297,8 @@ func createTestWallet(miningNode *rpctest.Harness, netParams *chaincfg.Params) (
return "", nil, err return "", nil, err
} }
cdb.RegisterCryptoSystem(&waddrmgrEncryptorDecryptor{wallet.Manager})
// Load our test wallet with 5 outputs each holding 4BTC. // Load our test wallet with 5 outputs each holding 4BTC.
if err := loadTestCredits(miningNode, wallet, 5, 4); err != nil { if err := loadTestCredits(miningNode, wallet, 5, 4); err != nil {
return "", nil, err return "", nil, err
@ -406,7 +418,7 @@ func testBasicWalletReservationWorkFlow(miner *rpctest.Harness, lnwallet *Lightn
// The resulting active channel state should have been persisted to the DB. // The resulting active channel state should have been persisted to the DB.
fundingTx := chanReservation.FinalFundingTx() fundingTx := chanReservation.FinalFundingTx()
channel, err := lnwallet.ChannelDB.FetchOpenChannel(bobNode.id) channel, err := lnwallet.channelDB.FetchOpenChannel(bobNode.id)
if err != nil { if err != nil {
t.Fatalf("unable to retrieve channel from DB: %v", err) t.Fatalf("unable to retrieve channel from DB: %v", err)
} }
@ -557,11 +569,10 @@ type testLnWallet struct {
cleanUpFunc func() cleanUpFunc func()
} }
func clearWalletState(w *LightningWallet) error { func clearWalletState(w *LightningWallet) {
w.nextFundingID = 0 w.nextFundingID = 0
w.fundingLimbo = make(map[uint64]*ChannelReservation) w.fundingLimbo = make(map[uint64]*ChannelReservation)
w.ResetLockedOutpoints() w.ResetLockedOutpoints()
return w.ChannelDB.Wipe()
} }
func TestLightningWallet(t *testing.T) { func TestLightningWallet(t *testing.T) {
@ -597,11 +608,8 @@ func TestLightningWallet(t *testing.T) {
for _, walletTest := range walletTests { for _, walletTest := range walletTests {
walletTest(miningNode, lnwallet, t) walletTest(miningNode, lnwallet, t)
if err := clearWalletState(lnwallet); err != nil && err != walletdb.ErrBucketNotFound {
t.Fatalf("unable to clear wallet state: %v", err)
}
// TODO(roasbeef): possible reset mining node's chainstate to // TODO(roasbeef): possible reset mining node's chainstate to
// initial level // initial level, cleanly wipe buckets
clearWalletState(lnwallet)
} }
} }