mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-23 06:35:07 +01:00
657 lines
18 KiB
Go
657 lines
18 KiB
Go
package chanbackup
|
|
|
|
import (
|
|
"bytes"
|
|
"math"
|
|
"math/rand"
|
|
"net"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/fn/v2"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnencrypt"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/shachain"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
chainHash = chainhash.Hash{
|
|
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
|
|
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
|
|
0x4f, 0x2f, 0x6f, 0x25, 0x18, 0xa3, 0xef, 0xb9,
|
|
0x64, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
|
|
}
|
|
|
|
op = wire.OutPoint{
|
|
Hash: chainHash,
|
|
Index: 4,
|
|
}
|
|
|
|
addr1, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9000")
|
|
addr2, _ = net.ResolveTCPAddr("tcp", "10.0.0.3:9000")
|
|
)
|
|
|
|
func assertSingleEqual(t *testing.T, a, b Single) {
|
|
t.Helper()
|
|
|
|
if a.Version != b.Version {
|
|
t.Fatalf("versions don't match: %v vs %v", a.Version,
|
|
b.Version)
|
|
}
|
|
if a.IsInitiator != b.IsInitiator {
|
|
t.Fatalf("initiators don't match: %v vs %v", a.IsInitiator,
|
|
b.IsInitiator)
|
|
}
|
|
if a.ChainHash != b.ChainHash {
|
|
t.Fatalf("chainhash doesn't match: %v vs %v", a.ChainHash,
|
|
b.ChainHash)
|
|
}
|
|
if a.FundingOutpoint != b.FundingOutpoint {
|
|
t.Fatalf("chan point doesn't match: %v vs %v",
|
|
a.FundingOutpoint, b.FundingOutpoint)
|
|
}
|
|
if a.ShortChannelID != b.ShortChannelID {
|
|
t.Fatalf("chan id doesn't match: %v vs %v",
|
|
a.ShortChannelID, b.ShortChannelID)
|
|
}
|
|
if a.Capacity != b.Capacity {
|
|
t.Fatalf("capacity doesn't match: %v vs %v",
|
|
a.Capacity, b.Capacity)
|
|
}
|
|
if !a.RemoteNodePub.IsEqual(b.RemoteNodePub) {
|
|
t.Fatalf("node pubs don't match %x vs %x",
|
|
a.RemoteNodePub.SerializeCompressed(),
|
|
b.RemoteNodePub.SerializeCompressed())
|
|
}
|
|
if !reflect.DeepEqual(a.LocalChanCfg, b.LocalChanCfg) {
|
|
t.Fatalf("local chan config doesn't match: %v vs %v",
|
|
spew.Sdump(a.LocalChanCfg),
|
|
spew.Sdump(b.LocalChanCfg))
|
|
}
|
|
if !reflect.DeepEqual(a.RemoteChanCfg, b.RemoteChanCfg) {
|
|
t.Fatalf("remote chan config doesn't match: %v vs %v",
|
|
spew.Sdump(a.RemoteChanCfg),
|
|
spew.Sdump(b.RemoteChanCfg))
|
|
}
|
|
if !reflect.DeepEqual(a.ShaChainRootDesc, b.ShaChainRootDesc) {
|
|
t.Fatalf("sha chain point doesn't match: %v vs %v",
|
|
spew.Sdump(a.ShaChainRootDesc),
|
|
spew.Sdump(b.ShaChainRootDesc))
|
|
}
|
|
|
|
if len(a.Addresses) != len(b.Addresses) {
|
|
t.Fatalf("expected %v addrs got %v", len(a.Addresses),
|
|
len(b.Addresses))
|
|
}
|
|
for i := 0; i < len(a.Addresses); i++ {
|
|
if a.Addresses[i].String() != b.Addresses[i].String() {
|
|
t.Fatalf("addr mismatch: %v vs %v",
|
|
a.Addresses[i], b.Addresses[i])
|
|
}
|
|
}
|
|
|
|
// Make sure that CloseTxInputs are present either in both backups,
|
|
// or in none of them.
|
|
require.Equal(t, a.CloseTxInputs.IsSome(), b.CloseTxInputs.IsSome())
|
|
|
|
if a.CloseTxInputs.IsSome() {
|
|
// Cache CloseTxInputs into short variables.
|
|
ai := a.CloseTxInputs.UnwrapOrFail(t)
|
|
bi := b.CloseTxInputs.UnwrapOrFail(t)
|
|
|
|
// Compare serialized unsigned transactions.
|
|
var abuf, bbuf bytes.Buffer
|
|
require.NoError(t, ai.CommitTx.Serialize(&abuf))
|
|
require.NoError(t, bi.CommitTx.Serialize(&bbuf))
|
|
aBytes := abuf.Bytes()
|
|
bBytes := bbuf.Bytes()
|
|
require.Equal(t, aBytes, bBytes)
|
|
|
|
// Compare counterparty's signature and commit height.
|
|
require.Equal(t, ai.CommitSig, bi.CommitSig)
|
|
require.Equal(t, ai.CommitHeight, bi.CommitHeight)
|
|
require.Equal(t, ai.TapscriptRoot, bi.TapscriptRoot)
|
|
}
|
|
}
|
|
|
|
func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) {
|
|
var testPriv [32]byte
|
|
if _, err := rand.Read(testPriv[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, pub := btcec.PrivKeyFromBytes(testPriv[:])
|
|
|
|
var chanPoint wire.OutPoint
|
|
if _, err := rand.Read(chanPoint.Hash[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
chanPoint.Index = uint32(rand.Intn(math.MaxUint16))
|
|
|
|
var shaChainRoot [32]byte
|
|
if _, err := rand.Read(shaChainRoot[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
shaChainProducer := shachain.NewRevocationProducer(shaChainRoot)
|
|
|
|
var isInitiator bool
|
|
if rand.Int63()%2 == 0 {
|
|
isInitiator = true
|
|
}
|
|
|
|
chanType := channeldb.ChannelType(rand.Intn(1 << 12))
|
|
|
|
localCfg := channeldb.ChannelConfig{
|
|
ChannelStateBounds: channeldb.ChannelStateBounds{},
|
|
CommitmentParams: channeldb.CommitmentParams{
|
|
CsvDelay: uint16(rand.Int63()),
|
|
},
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamily(rand.Int63()),
|
|
Index: uint32(rand.Int63()),
|
|
},
|
|
},
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamily(rand.Int63()),
|
|
Index: uint32(rand.Int63()),
|
|
},
|
|
},
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamily(rand.Int63()),
|
|
Index: uint32(rand.Int63()),
|
|
},
|
|
},
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamily(rand.Int63()),
|
|
Index: uint32(rand.Int63()),
|
|
},
|
|
},
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
KeyLocator: keychain.KeyLocator{
|
|
Family: keychain.KeyFamily(rand.Int63()),
|
|
Index: uint32(rand.Int63()),
|
|
},
|
|
},
|
|
}
|
|
|
|
remoteCfg := channeldb.ChannelConfig{
|
|
CommitmentParams: channeldb.CommitmentParams{
|
|
CsvDelay: uint16(rand.Int63()),
|
|
},
|
|
MultiSigKey: keychain.KeyDescriptor{
|
|
PubKey: pub,
|
|
},
|
|
RevocationBasePoint: keychain.KeyDescriptor{
|
|
PubKey: pub,
|
|
},
|
|
PaymentBasePoint: keychain.KeyDescriptor{
|
|
PubKey: pub,
|
|
},
|
|
DelayBasePoint: keychain.KeyDescriptor{
|
|
PubKey: pub,
|
|
},
|
|
HtlcBasePoint: keychain.KeyDescriptor{
|
|
PubKey: pub,
|
|
},
|
|
}
|
|
|
|
var localCommit channeldb.ChannelCommitment
|
|
if chanType.IsTaproot() {
|
|
var commitSig [64]byte
|
|
if _, err := rand.Read(commitSig[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
localCommit = channeldb.ChannelCommitment{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: commitSig[:],
|
|
CommitHeight: rand.Uint64(),
|
|
}
|
|
}
|
|
|
|
var tapscriptRootOption fn.Option[chainhash.Hash]
|
|
if chanType.HasTapscriptRoot() {
|
|
var tapscriptRoot chainhash.Hash
|
|
if _, err := rand.Read(tapscriptRoot[:]); err != nil {
|
|
return nil, err
|
|
}
|
|
tapscriptRootOption = fn.Some(tapscriptRoot)
|
|
}
|
|
|
|
return &channeldb.OpenChannel{
|
|
ChainHash: chainHash,
|
|
ChanType: chanType,
|
|
IsInitiator: isInitiator,
|
|
FundingOutpoint: chanPoint,
|
|
ShortChannelID: lnwire.NewShortChanIDFromInt(
|
|
uint64(rand.Int63()),
|
|
),
|
|
ThawHeight: rand.Uint32(),
|
|
IdentityPub: pub,
|
|
LocalChanCfg: localCfg,
|
|
RemoteChanCfg: remoteCfg,
|
|
LocalCommitment: localCommit,
|
|
RevocationProducer: shaChainProducer,
|
|
TapscriptRoot: tapscriptRootOption,
|
|
}, nil
|
|
}
|
|
|
|
// TestVersionEncoding tests encoding and decoding of version byte.
|
|
func TestVersionEncoding(t *testing.T) {
|
|
cases := []struct {
|
|
version SingleBackupVersion
|
|
hasCloseTx bool
|
|
versionByte byte
|
|
}{
|
|
{
|
|
version: DefaultSingleVersion,
|
|
hasCloseTx: false,
|
|
versionByte: DefaultSingleVersion,
|
|
},
|
|
{
|
|
version: DefaultSingleVersion,
|
|
hasCloseTx: true,
|
|
versionByte: DefaultSingleVersion | closeTxVersionMask,
|
|
},
|
|
{
|
|
version: AnchorsCommitVersion,
|
|
hasCloseTx: false,
|
|
versionByte: AnchorsCommitVersion,
|
|
},
|
|
{
|
|
version: AnchorsCommitVersion,
|
|
hasCloseTx: true,
|
|
versionByte: AnchorsCommitVersion | closeTxVersionMask,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
gotVersionByte := tc.version.Encode(tc.hasCloseTx)
|
|
require.Equal(t, tc.versionByte, gotVersionByte)
|
|
|
|
gotVersion, gotHasCloseTx := DecodeVersion(tc.versionByte)
|
|
require.Equal(t, tc.version, gotVersion)
|
|
require.Equal(t, tc.hasCloseTx, gotHasCloseTx)
|
|
}
|
|
}
|
|
|
|
var sampleCommitTx = &wire.MsgTx{
|
|
TxIn: []*wire.TxIn{
|
|
{PreviousOutPoint: wire.OutPoint{Hash: [32]byte{1}}},
|
|
},
|
|
TxOut: []*wire.TxOut{
|
|
{Value: 1e8, PkScript: []byte("1")},
|
|
{Value: 2e8, PkScript: []byte("2")},
|
|
},
|
|
}
|
|
|
|
// TestSinglePackUnpack tests that we're able to unpack a previously packed
|
|
// channel backup.
|
|
func TestSinglePackUnpack(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Given our test pub key, we'll create an open channel shell that
|
|
// contains all the information we need to create a static channel
|
|
// backup.
|
|
channel, err := genRandomOpenChannelShell()
|
|
require.NoError(t, err, "unable to gen open channel")
|
|
|
|
singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
|
|
|
|
keyRing := &lnencrypt.MockKeyRing{}
|
|
|
|
versionTestCases := []struct {
|
|
// version is the pack/unpack version that we should use to
|
|
// decode/encode the final SCB.
|
|
version SingleBackupVersion
|
|
|
|
// closeTxInputs is the data needed to produce a force close tx.
|
|
closeTxInputs fn.Option[CloseTxInputs]
|
|
|
|
// valid tests us if this test case should pass or not.
|
|
valid bool
|
|
}{
|
|
// The default version, should pack/unpack with no problem.
|
|
{
|
|
version: DefaultSingleVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// The new tweakless version, should pack/unpack with no
|
|
// problem.
|
|
{
|
|
version: TweaklessCommitVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// The new anchor version, should pack/unpack with no
|
|
// problem.
|
|
{
|
|
version: AnchorsCommitVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// The new script enforced channel lease version should
|
|
// pack/unpack with no problem.
|
|
{
|
|
version: ScriptEnforcedLeaseVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// The new taproot channel version should
|
|
// pack/unpack with no problem.
|
|
{
|
|
version: SimpleTaprootVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// The new tapscript root channel version should pack/unpack
|
|
// with no problem.
|
|
{
|
|
version: TapscriptRootVersion,
|
|
valid: true,
|
|
},
|
|
|
|
// A non-default version, atm this should result in a failure.
|
|
{
|
|
version: 99,
|
|
valid: false,
|
|
},
|
|
|
|
// Versions with CloseTxInputs.
|
|
{
|
|
version: DefaultSingleVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: TweaklessCommitVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: AnchorsCommitVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: ScriptEnforcedLeaseVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: SimpleTaprootVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
CommitHeight: 42,
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: TapscriptRootVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
CommitHeight: 42,
|
|
TapscriptRoot: fn.Some(chainhash.Hash{1}),
|
|
}),
|
|
valid: true,
|
|
},
|
|
{
|
|
version: 99,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
}),
|
|
valid: false,
|
|
},
|
|
{
|
|
version: 99,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
CommitHeight: 42,
|
|
}),
|
|
valid: false,
|
|
},
|
|
{
|
|
version: TapscriptRootVersion,
|
|
closeTxInputs: fn.Some(CloseTxInputs{
|
|
CommitTx: sampleCommitTx,
|
|
CommitSig: []byte("signature"),
|
|
CommitHeight: 42,
|
|
// TapscriptRoot is not filled.
|
|
}),
|
|
valid: false,
|
|
},
|
|
}
|
|
for i, versionCase := range versionTestCases {
|
|
// First, we'll re-assign SCB version to what was indicated in
|
|
// the test case.
|
|
singleChanBackup.Version = versionCase.version
|
|
singleChanBackup.CloseTxInputs = versionCase.closeTxInputs
|
|
|
|
var b bytes.Buffer
|
|
|
|
err := singleChanBackup.PackToWriter(&b, keyRing)
|
|
switch {
|
|
// If this is a valid test case, and we failed, then we'll
|
|
// return an error.
|
|
case err != nil && versionCase.valid:
|
|
t.Fatalf("#%v, unable to pack single: %v", i, err)
|
|
|
|
// If this is an invalid test case, and we passed it, then
|
|
// we'll return an error.
|
|
case err == nil && !versionCase.valid:
|
|
t.Fatalf("#%v got nil error for invalid pack: %v",
|
|
i, err)
|
|
}
|
|
|
|
// If this is a valid test case, then we'll continue to ensure
|
|
// we can unpack it, and also that if we mutate the packed
|
|
// version, then we trigger an error.
|
|
if versionCase.valid {
|
|
var unpackedSingle Single
|
|
err = unpackedSingle.UnpackFromReader(&b, keyRing)
|
|
if err != nil {
|
|
t.Fatalf("#%v unable to unpack single: %v",
|
|
i, err)
|
|
}
|
|
|
|
assertSingleEqual(t, singleChanBackup, unpackedSingle)
|
|
|
|
// If this was a valid packing attempt, then we'll test
|
|
// to ensure that if we mutate the version prepended to
|
|
// the serialization, then unpacking will fail as well.
|
|
var rawSingle bytes.Buffer
|
|
err := unpackedSingle.Serialize(&rawSingle)
|
|
if err != nil {
|
|
t.Fatalf("unable to serialize single: %v", err)
|
|
}
|
|
|
|
// Mutate the version byte to an unknown version.
|
|
rawBytes := rawSingle.Bytes()
|
|
rawBytes[0] = ^uint8(0)
|
|
|
|
newReader := bytes.NewReader(rawBytes)
|
|
err = unpackedSingle.Deserialize(newReader)
|
|
if err == nil {
|
|
t.Fatalf("#%v unpack with unknown version "+
|
|
"should have failed", i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPackedSinglesUnpack tests that we're able to properly unpack a series of
|
|
// packed singles.
|
|
func TestPackedSinglesUnpack(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
keyRing := &lnencrypt.MockKeyRing{}
|
|
|
|
// To start, we'll create 10 new singles, and them assemble their
|
|
// packed forms into a slice.
|
|
numSingles := 10
|
|
packedSingles := make([][]byte, 0, numSingles)
|
|
unpackedSingles := make([]Single, 0, numSingles)
|
|
for i := 0; i < numSingles; i++ {
|
|
channel, err := genRandomOpenChannelShell()
|
|
if err != nil {
|
|
t.Fatalf("unable to gen channel: %v", err)
|
|
}
|
|
|
|
single := NewSingle(channel, nil)
|
|
|
|
var b bytes.Buffer
|
|
if err := single.PackToWriter(&b, keyRing); err != nil {
|
|
t.Fatalf("unable to pack single: %v", err)
|
|
}
|
|
|
|
packedSingles = append(packedSingles, b.Bytes())
|
|
unpackedSingles = append(unpackedSingles, single)
|
|
}
|
|
|
|
// With all singles packed, we'll create the grouped type and attempt
|
|
// to Unpack all of them in a single go.
|
|
freshSingles, err := PackedSingles(packedSingles).Unpack(keyRing)
|
|
require.NoError(t, err, "unable to unpack singles")
|
|
|
|
// The set of freshly unpacked singles should exactly match the initial
|
|
// set of singles that we packed before.
|
|
for i := 0; i < len(unpackedSingles); i++ {
|
|
assertSingleEqual(t, unpackedSingles[i], freshSingles[i])
|
|
}
|
|
|
|
// If we mutate one of the packed singles, then the entire method
|
|
// should fail.
|
|
packedSingles[0][0] ^= 1
|
|
_, err = PackedSingles(packedSingles).Unpack(keyRing)
|
|
if err == nil {
|
|
t.Fatalf("unpack attempt should fail")
|
|
}
|
|
}
|
|
|
|
// TestSinglePackStaticChanBackups tests that we're able to batch pack a set of
|
|
// Singles, and then unpack them obtaining the same set of unpacked singles.
|
|
func TestSinglePackStaticChanBackups(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
keyRing := &lnencrypt.MockKeyRing{}
|
|
|
|
// First, we'll create a set of random single, and along the way,
|
|
// create a map that will let us look up each single by its chan point.
|
|
numSingles := 10
|
|
singleMap := make(map[wire.OutPoint]Single, numSingles)
|
|
unpackedSingles := make([]Single, 0, numSingles)
|
|
for i := 0; i < numSingles; i++ {
|
|
channel, err := genRandomOpenChannelShell()
|
|
if err != nil {
|
|
t.Fatalf("unable to gen channel: %v", err)
|
|
}
|
|
|
|
single := NewSingle(channel, nil)
|
|
|
|
singleMap[channel.FundingOutpoint] = single
|
|
unpackedSingles = append(unpackedSingles, single)
|
|
}
|
|
|
|
// Now that we have all of our singles are created, we'll attempt to
|
|
// pack them all in a single batch.
|
|
packedSingleMap, err := PackStaticChanBackups(unpackedSingles, keyRing)
|
|
require.NoError(t, err, "unable to pack backups")
|
|
|
|
// With our packed singles obtained, we'll ensure that each of them
|
|
// match their unpacked counterparts after they themselves have been
|
|
// unpacked.
|
|
for chanPoint, single := range singleMap {
|
|
packedSingles, ok := packedSingleMap[chanPoint]
|
|
if !ok {
|
|
t.Fatalf("unable to find single %v", chanPoint)
|
|
}
|
|
|
|
var freshSingle Single
|
|
err := freshSingle.UnpackFromReader(
|
|
bytes.NewReader(packedSingles), keyRing,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unable to unpack single: %v", err)
|
|
}
|
|
|
|
assertSingleEqual(t, single, freshSingle)
|
|
}
|
|
|
|
// If we attempt to pack again, but force the key ring to fail, then
|
|
// the entire method should fail.
|
|
keyRing.Fail = true
|
|
_, err = PackStaticChanBackups(
|
|
unpackedSingles, &lnencrypt.MockKeyRing{Fail: true},
|
|
)
|
|
if err == nil {
|
|
t.Fatalf("pack attempt should fail")
|
|
}
|
|
}
|
|
|
|
// TestSingleUnconfirmedChannel tests that unconfirmed channels get serialized
|
|
// correctly by encoding the funding broadcast height as block height of the
|
|
// short channel ID.
|
|
func TestSingleUnconfirmedChannel(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var fundingBroadcastHeight = uint32(1234)
|
|
|
|
// Let's create an open channel shell that contains all the information
|
|
// we need to create a static channel backup but simulate an
|
|
// unconfirmed channel by setting the block height to 0.
|
|
channel, err := genRandomOpenChannelShell()
|
|
require.NoError(t, err, "unable to gen open channel")
|
|
channel.ShortChannelID.BlockHeight = 0
|
|
channel.FundingBroadcastHeight = fundingBroadcastHeight
|
|
|
|
singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
|
|
keyRing := &lnencrypt.MockKeyRing{}
|
|
|
|
// Pack it and then unpack it again to make sure everything is written
|
|
// correctly, then check that the block height of the unpacked
|
|
// is the funding broadcast height we set before.
|
|
var b bytes.Buffer
|
|
if err := singleChanBackup.PackToWriter(&b, keyRing); err != nil {
|
|
t.Fatalf("unable to pack single: %v", err)
|
|
}
|
|
var unpackedSingle Single
|
|
err = unpackedSingle.UnpackFromReader(&b, keyRing)
|
|
require.NoError(t, err, "unable to unpack single")
|
|
if unpackedSingle.ShortChannelID.BlockHeight != fundingBroadcastHeight {
|
|
t.Fatalf("invalid block height. got %d expected %d.",
|
|
unpackedSingle.ShortChannelID.BlockHeight,
|
|
fundingBroadcastHeight)
|
|
}
|
|
}
|
|
|
|
// TODO(roasbsef): fuzz parsing
|