channeldb: BigSize migration, store zero-conf, scid-alias bits

This introduces a BigSize migration that is used to expand the width
of the ChannelStatus and ChannelType fields. Three channel "types"
are added - ZeroConfBit, ScidAliasChanBit, and ScidAliasFeatureBit.
ScidAliasChanBit denotes that the scid-alias channel type was
negotiated for the channel. ScidAliasFeatureBit denotes that the
scid-alias feature bit was negotiated during the *lifetime* of the
channel. Several helper functions on the OpenChannel struct are
exposed to aid callers from different packages.

The RefreshShortChanID has been renamed to Refresh.

A new function BroadcastHeight is used to guard access to the
mutable FundingBroadcastHeight member. This prevents data races.
This commit is contained in:
eugene committed 2022-07-07 17:10:27 -04:00
1 parent 3ff8eb899c
commit c9f5912601
13 files changed
+503 -28

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
package migration29
import (
"encoding/binary"
"encoding/hex"
"io"
"github.com/btcsuite/btcd/wire"
)
var (
byteOrder = binary.BigEndian
)
// ChannelID is a series of 32-bytes that uniquely identifies all channels
// within the network. The ChannelID is computed using the outpoint of the
// funding transaction (the txid, and output index). Given a funding output the
// ChannelID can be calculated by XOR'ing the big-endian serialization of the
// txid and the big-endian serialization of the output index, truncated to
// 2 bytes.
type ChannelID [32]byte
// String returns the string representation of the ChannelID. This is just the
// hex string encoding of the ChannelID itself.
func (c ChannelID) String() string {
return hex.EncodeToString(c[:])
}
// NewChanIDFromOutPoint converts a target OutPoint into a ChannelID that is
// usable within the network. In order to convert the OutPoint into a ChannelID,
// we XOR the lower 2-bytes of the txid within the OutPoint with the big-endian
// serialization of the Index of the OutPoint, truncated to 2-bytes.
func NewChanIDFromOutPoint(op *wire.OutPoint) ChannelID {
// First we'll copy the txid of the outpoint into our channel ID slice.
var cid ChannelID
copy(cid[:], op.Hash[:])
// With the txid copied over, we'll now XOR the lower 2-bytes of the
// partial channelID with big-endian serialization of output index.
xorTxid(&cid, uint16(op.Index))
return cid
}
// xorTxid performs the transformation needed to transform an OutPoint into a
// ChannelID. To do this, we expect the cid parameter to contain the txid
// unaltered and the outputIndex to be the output index
func xorTxid(cid *ChannelID, outputIndex uint16) {
var buf [2]byte
binary.BigEndian.PutUint16(buf[:], outputIndex)
cid[30] ^= buf[0]
cid[31] ^= buf[1]
}
// readOutpoint reads an outpoint from the passed reader.
func readOutpoint(r io.Reader, o *wire.OutPoint) error {
if _, err := io.ReadFull(r, o.Hash[:]); err != nil {
return err
}
if err := binary.Read(r, byteOrder, &o.Index); err != nil {
return err
}
return nil
}
+12
View File
@@ -0,0 +1,12 @@
package migration29
import "github.com/btcsuite/btclog"
// log is a logger that is initialized as disabled. This means the package will
// not perform any logging by default until a logger is set.
var log = btclog.Disabled
// UseLogger uses a specific Logger to output package logging info.
func UseLogger(logger btclog.Logger) {
log = logger
}
+72
View File
@@ -0,0 +1,72 @@
package migration29
import (
"bytes"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
// outpointBucket is the bucket that stores the set of outpoints we
// know about.
outpointBucket = []byte("outpoint-bucket")
// chanIDBucket is the bucket that stores the set of ChannelID's we
// know about.
chanIDBucket = []byte("chan-id-bucket")
)
// MigrateChanID populates the ChannelID index by using the set of outpoints
// retrieved from the outpoint bucket.
func MigrateChanID(tx kvdb.RwTx) error {
log.Info("Populating ChannelID index")
// First we'll retrieve the set of outpoints we know about.
ops, err := fetchOutPoints(tx)
if err != nil {
return err
}
return populateChanIDIndex(tx, ops)
}
// fetchOutPoints loops through the outpointBucket and returns each stored
// outpoint.
func fetchOutPoints(tx kvdb.RwTx) ([]*wire.OutPoint, error) {
var ops []*wire.OutPoint
bucket := tx.ReadBucket(outpointBucket)
err := bucket.ForEach(func(k, _ []byte) error {
var op wire.OutPoint
r := bytes.NewReader(k)
if err := readOutpoint(r, &op); err != nil {
return err
}
ops = append(ops, &op)
return nil
})
if err != nil {
return nil, err
}
return ops, nil
}
// populateChanIDIndex uses the set of retrieved outpoints and populates the
// ChannelID index.
func populateChanIDIndex(tx kvdb.RwTx, ops []*wire.OutPoint) error {
bucket := tx.ReadWriteBucket(chanIDBucket)
for _, op := range ops {
chanID := NewChanIDFromOutPoint(op)
if err := bucket.Put(chanID[:], []byte{}); err != nil {
return err
}
}
return nil
}
+67
View File
@@ -0,0 +1,67 @@
package migration29
import (
"testing"
"github.com/lightningnetwork/lnd/channeldb/migtest"
"github.com/lightningnetwork/lnd/kvdb"
)
var (
hexStr = migtest.Hex
outpoint1 = hexStr("81b637d8fcd2c6da6859e6963113a1170de793e4b725b84d1e0b4cf99ec58ce90fb463ad")
outpoint2 = hexStr("81b637d8fcd2c6da6859e6963113a1170de793e4b725b84d1e0b4cf99ec58ce952d6c6c7")
chanID1 = hexStr("81b637d8fcd2c6da6859e6963113a1170de793e4b725b84d1e0b4cf99ec5ef44")
chanID2 = hexStr("81b637d8fcd2c6da6859e6963113a1170de793e4b725b84d1e0b4cf99ec54a2e")
// These tlv streams are used to populate the outpoint bucket at the
// start of the test.
tlvOutpointOpen = hexStr("000100")
tlvOutpointClosed = hexStr("000101")
// outpointData is used to populate the outpoint bucket.
outpointData = map[string]interface{}{
outpoint1: tlvOutpointOpen,
outpoint2: tlvOutpointClosed,
}
// chanIDBefore is the ChannelID bucket before the migration.
chanIDBefore = map[string]interface{}{}
// post is the expected data in the ChannelID bucket after the
// migration.
post = map[string]interface{}{
chanID1: "",
chanID2: "",
}
)
// TestMigrateChannelIDIndex asserts that the ChannelID index is properly
// populated.
func TestMigrateChannelIDIndex(t *testing.T) {
// Prime the database with the populated outpoint bucket. We create the
// ChannelID bucket since the prior migration creates it anyways.
before := func(tx kvdb.RwTx) error {
err := migtest.RestoreDB(tx, outpointBucket, outpointData)
if err != nil {
return err
}
return migtest.RestoreDB(tx, chanIDBucket, chanIDBefore)
}
// After the migration, ensure that the ChannelID bucket is properly
// populated.
after := func(tx kvdb.RwTx) error {
err := migtest.VerifyDB(tx, outpointBucket, outpointData)
if err != nil {
return err
}
return migtest.VerifyDB(tx, chanIDBucket, post)
}
migtest.ApplyMigration(t, before, after, MigrateChanID, false)
}