channeldb: update tests to reflect latest OpenChannel/HTLC API changes

This commit is contained in:
Olaoluwa Osuntokun 2017-07-29 11:24:28 -07:00
parent 5854ffb644
commit 5ad6c23848
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -3,11 +3,11 @@ package channeldb
import (
"bytes"
"io/ioutil"
"math/rand"
"net"
"os"
"reflect"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lnwire"
@ -15,7 +15,6 @@ import (
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
_ "github.com/roasbeef/btcwallet/walletdb/bdb"
@ -109,23 +108,13 @@ func makeTestDB() (*DB, func(), error) {
}
func createTestChannelState(cdb *DB) (*OpenChannel, error) {
addr, err := btcutil.NewAddressPubKey(pubKey.SerializeCompressed(), netParams)
if err != nil {
return nil, err
}
script, err := txscript.MultiSigScript([]*btcutil.AddressPubKey{addr, addr}, 2)
if err != nil {
return nil, err
}
// Simulate 1000 channel updates.
producer, err := shachain.NewRevocationProducerFromBytes(key[:])
if err != nil {
return nil, err
}
store := shachain.NewRevocationStore()
for i := 0; i < 1000; i++ {
for i := 0; i < 1; i++ {
preImage, err := producer.AtIndex(uint64(i))
if err != nil {
return nil, err
@ -139,41 +128,63 @@ func createTestChannelState(cdb *DB) (*OpenChannel, error) {
var obsfucator [6]byte
copy(obsfucator[:], key[:])
localCfg := ChannelConfig{
ChannelConstraints: ChannelConstraints{
DustLimit: btcutil.Amount(rand.Int63()),
MaxPendingAmount: btcutil.Amount(rand.Int63()),
ChanReserve: btcutil.Amount(rand.Int63()),
MinHTLC: btcutil.Amount(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()),
},
CsvDelay: uint16(rand.Int31()),
MultiSigKey: privKey.PubKey(),
RevocationBasePoint: privKey.PubKey(),
PaymentBasePoint: privKey.PubKey(),
DelayBasePoint: privKey.PubKey(),
}
remoteCfg := ChannelConfig{
ChannelConstraints: ChannelConstraints{
DustLimit: btcutil.Amount(rand.Int63()),
MaxPendingAmount: btcutil.Amount(rand.Int63()),
ChanReserve: btcutil.Amount(rand.Int63()),
MinHTLC: btcutil.Amount(rand.Int63()),
MaxAcceptedHtlcs: uint16(rand.Int31()),
},
CsvDelay: uint16(rand.Int31()),
MultiSigKey: privKey.PubKey(),
RevocationBasePoint: privKey.PubKey(),
PaymentBasePoint: privKey.PubKey(),
DelayBasePoint: privKey.PubKey(),
}
chanID := lnwire.NewShortChanIDFromInt(uint64(rand.Int63()))
return &OpenChannel{
IsInitiator: true,
IsPending: true,
ChanType: SingleFunder,
IdentityPub: pubKey,
ChanID: id,
FeePerKw: btcutil.Amount(5000),
TheirDustLimit: btcutil.Amount(200),
OurDustLimit: btcutil.Amount(200),
OurCommitKey: privKey.PubKey(),
TheirCommitKey: pubKey,
Capacity: btcutil.Amount(10000),
OurBalance: btcutil.Amount(3000),
TheirBalance: btcutil.Amount(9000),
OurCommitTx: testTx,
OurCommitSig: bytes.Repeat([]byte{1}, 71),
RevocationProducer: producer,
RevocationStore: store,
StateHintObsfucator: obsfucator,
FundingOutpoint: testOutpoint,
OurMultiSigKey: privKey.PubKey(),
TheirMultiSigKey: privKey.PubKey(),
FundingWitnessScript: script,
NumConfsRequired: 4,
TheirCurrentRevocation: privKey.PubKey(),
TheirCurrentRevocationHash: key,
OurDeliveryScript: script,
TheirDeliveryScript: script,
LocalCsvDelay: 5,
RemoteCsvDelay: 9,
NumUpdates: 0,
TotalSatoshisSent: 8,
TotalSatoshisReceived: 2,
CreationTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
Db: cdb,
ChanType: SingleFunder,
ChainHash: key,
FundingOutpoint: *testOutpoint,
ShortChanID: chanID,
IsInitiator: true,
IsPending: true,
IdentityPub: pubKey,
LocalChanCfg: localCfg,
RemoteChanCfg: remoteCfg,
CommitFee: btcutil.Amount(rand.Int63()),
FeePerKw: btcutil.Amount(5000),
Capacity: btcutil.Amount(10000),
LocalBalance: btcutil.Amount(3000),
RemoteBalance: btcutil.Amount(9000),
CommitTx: *testTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
NumConfsRequired: 4,
RemoteCurrentRevocation: privKey.PubKey(),
RemoteNextRevocation: privKey.PubKey(),
RevocationProducer: producer,
RevocationStore: store,
NumUpdates: 0,
TotalSatoshisSent: 8,
TotalSatoshisReceived: 2,
Db: cdb,
}, nil
}
@ -194,6 +205,7 @@ func TestOpenChannelPutGetDelete(t *testing.T) {
}
state.Htlcs = []*HTLC{
{
Signature: testSig.Serialize(),
Incoming: true,
Amt: 10,
RHash: key,
@ -214,161 +226,58 @@ func TestOpenChannelPutGetDelete(t *testing.T) {
// The decoded channel state should be identical to what we stored
// above.
if !state.IdentityPub.IsEqual(newState.IdentityPub) {
t.Fatal("their id doesn't match")
}
if !reflect.DeepEqual(state.ChanID, newState.ChanID) {
t.Fatal("chan id's don't match")
}
if state.FeePerKw != newState.FeePerKw {
t.Fatal("fee/kb doesn't match")
}
if state.TheirDustLimit != newState.TheirDustLimit {
t.Fatal("their dust limit doesn't match")
}
if state.OurDustLimit != newState.OurDustLimit {
t.Fatal("our dust limit doesn't match")
}
if state.IsInitiator != newState.IsInitiator {
t.Fatal("initiator status doesn't match")
}
if state.ChanType != newState.ChanType {
t.Fatal("channel type doesn't match")
if !reflect.DeepEqual(state, newState) {
state.LocalChanCfg.MultiSigKey.Curve = nil
state.LocalChanCfg.RevocationBasePoint.Curve = nil
state.LocalChanCfg.PaymentBasePoint.Curve = nil
state.LocalChanCfg.DelayBasePoint.Curve = nil
state.RemoteChanCfg.MultiSigKey.Curve = nil
state.RemoteChanCfg.RevocationBasePoint.Curve = nil
state.RemoteChanCfg.PaymentBasePoint.Curve = nil
state.RemoteChanCfg.DelayBasePoint.Curve = nil
state.IdentityPub.Curve = nil
state.RemoteNextRevocation.Curve = nil
state.RemoteCurrentRevocation.Curve = nil
newState.LocalChanCfg.MultiSigKey.Curve = nil
newState.LocalChanCfg.RevocationBasePoint.Curve = nil
newState.LocalChanCfg.PaymentBasePoint.Curve = nil
newState.LocalChanCfg.DelayBasePoint.Curve = nil
newState.RemoteChanCfg.MultiSigKey.Curve = nil
newState.RemoteChanCfg.RevocationBasePoint.Curve = nil
newState.RemoteChanCfg.PaymentBasePoint.Curve = nil
newState.RemoteChanCfg.DelayBasePoint.Curve = nil
newState.IdentityPub.Curve = nil
newState.RemoteCurrentRevocation.Curve = nil
newState.RemoteNextRevocation.Curve = nil
t.Fatalf("channel state doesn't match:: %v vs %v",
spew.Sdump(state), spew.Sdump(newState))
}
if !bytes.Equal(state.OurCommitKey.SerializeCompressed(),
newState.OurCommitKey.SerializeCompressed()) {
t.Fatal("our commit key doesn't match")
}
if !bytes.Equal(state.TheirCommitKey.SerializeCompressed(),
newState.TheirCommitKey.SerializeCompressed()) {
t.Fatal("their commit key doesn't match")
}
if state.Capacity != newState.Capacity {
t.Fatalf("capacity doesn't match: %v vs %v", state.Capacity,
newState.Capacity)
}
if state.OurBalance != newState.OurBalance {
t.Fatal("our balance doesn't match")
}
if state.TheirBalance != newState.TheirBalance {
t.Fatal("their balance doesn't match")
}
var b1, b2 bytes.Buffer
if err := state.OurCommitTx.Serialize(&b1); err != nil {
t.Fatal("unable to serialize transaction")
}
if err := newState.OurCommitTx.Serialize(&b2); err != nil {
t.Fatal("unable to serialize transaction")
}
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
t.Fatal("ourCommitTx doesn't match")
}
if !bytes.Equal(newState.OurCommitSig, state.OurCommitSig) {
t.Fatal("commit sigs don't match")
}
// TODO(roasbeef): replace with a single equal?
if !reflect.DeepEqual(state.FundingOutpoint, newState.FundingOutpoint) {
t.Fatal("funding outpoint doesn't match")
}
if !bytes.Equal(state.OurMultiSigKey.SerializeCompressed(),
newState.OurMultiSigKey.SerializeCompressed()) {
t.Fatal("our multisig key doesn't match")
}
if !bytes.Equal(state.TheirMultiSigKey.SerializeCompressed(),
newState.TheirMultiSigKey.SerializeCompressed()) {
t.Fatal("their multisig key doesn't match")
}
if !bytes.Equal(state.FundingWitnessScript, newState.FundingWitnessScript) {
t.Fatal("redeem script doesn't match")
}
// The local and remote delivery scripts should be identical.
if !bytes.Equal(state.OurDeliveryScript, newState.OurDeliveryScript) {
t.Fatal("our delivery address doesn't match")
}
if !bytes.Equal(state.TheirDeliveryScript, newState.TheirDeliveryScript) {
t.Fatal("their delivery address doesn't match")
}
if state.NumUpdates != newState.NumUpdates {
t.Fatalf("num updates doesn't match: %v vs %v",
state.NumUpdates, newState.NumUpdates)
}
if state.RemoteCsvDelay != newState.RemoteCsvDelay {
t.Fatalf("csv delay doesn't match: %v vs %v",
state.RemoteCsvDelay, newState.RemoteCsvDelay)
}
if state.LocalCsvDelay != newState.LocalCsvDelay {
t.Fatalf("csv delay doesn't match: %v vs %v",
state.LocalCsvDelay, newState.LocalCsvDelay)
}
if state.TotalSatoshisSent != newState.TotalSatoshisSent {
t.Fatalf("satoshis sent doesn't match: %v vs %v",
state.TotalSatoshisSent, newState.TotalSatoshisSent)
}
if state.TotalSatoshisReceived != newState.TotalSatoshisReceived {
t.Fatal("satoshis received doesn't match")
}
if state.NumConfsRequired != newState.NumConfsRequired {
t.Fatalf("num confs required doesn't match: %v, vs. %v",
state.NumConfsRequired, newState.NumConfsRequired)
}
if state.CreationTime.Unix() != newState.CreationTime.Unix() {
t.Fatal("creation time doesn't match")
}
// The local and remote producers should be identical.
var old bytes.Buffer
err = state.RevocationProducer.Encode(&old)
// We'll also test that the channel is properly able to hot swap the
// next revocation for the state machine. This tests the initial
// post-funding revocation exchange.
nextRevKey, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Fatalf("can't convert old revocation producer to bytes: %v",
err)
t.Fatalf("unable to create new private key: %v", err)
}
if err := state.InsertNextRevocation(nextRevKey.PubKey()); err != nil {
t.Fatalf("unable to update revocation: %v", err)
}
var new bytes.Buffer
err = newState.RevocationProducer.Encode(&new)
openChannels, err = cdb.FetchOpenChannels(state.IdentityPub)
if err != nil {
t.Fatalf("can't convert new revocation producer to bytes: %v",
err)
t.Fatalf("unable to fetch open channel: %v", err)
}
updatedChan := openChannels[0]
if !bytes.Equal(old.Bytes(), new.Bytes()) {
t.Fatal("local producer don't match")
}
old.Reset()
new.Reset()
err = state.RevocationStore.Encode(&old)
if err != nil {
t.Fatalf("unable to serialize old remote store: %v", err)
}
err = newState.RevocationStore.Encode(&new)
if err != nil {
t.Fatalf("unable to serialize new remote store: %v", err)
}
if !bytes.Equal(old.Bytes(), new.Bytes()) {
t.Fatal("remote store don't match")
}
if !newState.TheirCurrentRevocation.IsEqual(state.TheirCurrentRevocation) {
t.Fatal("revocation keys don't match")
}
if !bytes.Equal(newState.TheirCurrentRevocationHash[:], state.TheirCurrentRevocationHash[:]) {
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]),
spew.Sdump(newState.Htlcs[0]))
}
if !bytes.Equal(state.StateHintObsfucator[:],
newState.StateHintObsfucator[:]) {
t.Fatal("obsfuctators don't match")
// Ensure that the revocation was set properly.
if !nextRevKey.PubKey().IsEqual(updatedChan.RemoteNextRevocation) {
t.Fatalf("next revocation wasn't updated")
}
// Finally to wrap up the test, delete the state of the channel within
@ -376,7 +285,7 @@ func TestOpenChannelPutGetDelete(t *testing.T) {
// written state, and creates a small "summary" elsewhere within the
// database.
closeSummary := &ChannelCloseSummary{
ChanPoint: *state.ChanID,
ChanPoint: state.FundingOutpoint,
RemotePub: state.IdentityPub,
SettledBalance: btcutil.Amount(500),
TimeLockedBalance: btcutil.Amount(10000),
@ -439,12 +348,13 @@ func TestChannelStateTransition(t *testing.T) {
incoming = true
}
htlc := &HTLC{
Signature: testSig.Serialize(),
Incoming: incoming,
Amt: 10,
RHash: key,
RefundTimeout: i,
RevocationDelay: i + 2,
OutputIndex: uint16(i * 3),
OutputIndex: int32(i * 3),
}
htlcs = append(htlcs, htlc)
htlcAmt += htlc.Amt
@ -455,7 +365,7 @@ func TestChannelStateTransition(t *testing.T) {
// Additionally, modify the signature and commitment transaction.
newSequence := uint32(129498)
newSig := bytes.Repeat([]byte{3}, 71)
newTx := channel.OurCommitTx.Copy()
newTx := channel.CommitTx.Copy()
newTx.TxIn[0].Sequence = newSequence
delta := &ChannelDelta{
LocalBalance: btcutil.Amount(1e8),
@ -476,21 +386,21 @@ func TestChannelStateTransition(t *testing.T) {
if err != nil {
t.Fatalf("unable to fetch updated channel: %v", err)
}
if !bytes.Equal(updatedChannel[0].OurCommitSig, newSig) {
if !bytes.Equal(updatedChannel[0].CommitSig, newSig) {
t.Fatalf("sigs don't match %x vs %x",
updatedChannel[0].OurCommitSig, newSig)
updatedChannel[0].CommitSig, newSig)
}
if updatedChannel[0].OurCommitTx.TxIn[0].Sequence != newSequence {
if updatedChannel[0].CommitTx.TxIn[0].Sequence != newSequence {
t.Fatalf("sequence numbers don't match: %v vs %v",
updatedChannel[0].OurCommitTx.TxIn[0].Sequence, newSequence)
updatedChannel[0].CommitTx.TxIn[0].Sequence, newSequence)
}
if updatedChannel[0].OurBalance != delta.LocalBalance {
if updatedChannel[0].LocalBalance != delta.LocalBalance {
t.Fatalf("local balances don't match: %v vs %v",
updatedChannel[0].OurBalance, delta.LocalBalance)
updatedChannel[0].LocalBalance, delta.LocalBalance)
}
if updatedChannel[0].TheirBalance != delta.RemoteBalance {
if updatedChannel[0].RemoteBalance != delta.RemoteBalance {
t.Fatalf("remote balances don't match: %v vs %v",
updatedChannel[0].TheirBalance, delta.RemoteBalance)
updatedChannel[0].RemoteBalance, delta.RemoteBalance)
}
if updatedChannel[0].NumUpdates != uint64(delta.UpdateNum) {
t.Fatalf("update # doesn't match: %v vs %v",
@ -518,8 +428,12 @@ func TestChannelStateTransition(t *testing.T) {
// needed to rectify any fishy behavior by the remote party. Modify the
// current uncollapsed revocation state to simulate a state transition
// by the remote party.
newRevocation := bytes.Repeat([]byte{9}, 32)
copy(channel.TheirCurrentRevocationHash[:], newRevocation)
channel.RemoteCurrentRevocation = channel.RemoteNextRevocation
newPriv, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Fatalf("unable to generate key: %v", err)
}
channel.RemoteNextRevocation = newPriv.PubKey()
if err := channel.AppendToRevocationLog(delta); err != nil {
t.Fatalf("unable to append to revocation log: %v", err)
}
@ -604,14 +518,16 @@ func TestChannelStateTransition(t *testing.T) {
if err != nil {
t.Fatalf("unable to fetch updated channel: %v", err)
}
if !bytes.Equal(updatedChannel[0].TheirCurrentRevocationHash[:],
newRevocation) {
t.Fatal("revocation state wasn't synced!")
if !channel.RemoteCurrentRevocation.IsEqual(updatedChannel[0].RemoteCurrentRevocation) {
t.Fatalf("revocation state was not synced")
}
if !channel.RemoteNextRevocation.IsEqual(updatedChannel[0].RemoteNextRevocation) {
t.Fatalf("revocation state was not synced")
}
// Now attempt to delete the channel from the database.
closeSummary := &ChannelCloseSummary{
ChanPoint: *channel.ChanID,
ChanPoint: channel.FundingOutpoint,
RemotePub: channel.IdentityPub,
SettledBalance: btcutil.Amount(500),
TimeLockedBalance: btcutil.Amount(10000),
@ -689,7 +605,7 @@ func TestFetchPendingChannels(t *testing.T) {
TxIndex: 10,
TxPosition: 15,
}
err = cdb.MarkChannelAsOpen(pendingChannels[0].ChanID, chanOpenLoc)
err = cdb.MarkChannelAsOpen(&pendingChannels[0].FundingOutpoint, chanOpenLoc)
if err != nil {
t.Fatalf("unable to mark channel as open: %v", err)
}
@ -756,19 +672,20 @@ func TestFetchClosedChannels(t *testing.T) {
TxIndex: 10,
TxPosition: 15,
}
if err := cdb.MarkChannelAsOpen(state.ChanID, chanOpenLoc); err != nil {
err = cdb.MarkChannelAsOpen(&state.FundingOutpoint, chanOpenLoc)
if err != nil {
t.Fatalf("unable to mark channel as open: %v", err)
}
// Next, close the channel by including a close channel summary in the
// database.
summary := &ChannelCloseSummary{
ChanPoint: *state.ChanID,
ChanPoint: state.FundingOutpoint,
ClosingTXID: rev,
RemotePub: state.IdentityPub,
Capacity: state.Capacity,
SettledBalance: state.OurBalance,
TimeLockedBalance: state.OurBalance + 10000,
SettledBalance: state.LocalBalance,
TimeLockedBalance: state.LocalBalance + 10000,
CloseType: ForceClose,
IsPending: true,
}
@ -805,7 +722,8 @@ func TestFetchClosedChannels(t *testing.T) {
}
// Mark the channel as fully closed
if err := cdb.MarkChanFullyClosed(state.ChanID); err != nil {
err = cdb.MarkChanFullyClosed(&state.FundingOutpoint)
if err != nil {
t.Fatalf("failed fully closing channel: %v", err)
}