Skip to content

Commit

Permalink
Merge pull request #3633 from joostjager/safe-migrations
Browse files Browse the repository at this point in the history
channeldb: isolate migrations
  • Loading branch information
Roasbeef committed Nov 2, 2019
2 parents bef1cd0 + 4486a06 commit 38e313a
Show file tree
Hide file tree
Showing 27 changed files with 4,731 additions and 176 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ run:
skip-files:
- "mobile\\/.*generated\\.go"

skip-dirs:
- channeldb/migration_01_to_11

build-tags:
- autopilotrpc
- chainrpc
Expand Down
27 changes: 12 additions & 15 deletions channeldb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/coreos/bbolt"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
"github.com/lightningnetwork/lnd/lnwire"
)

Expand Down Expand Up @@ -47,73 +48,73 @@ var (
// for the update time of node and channel updates were
// added.
number: 1,
migration: migrateNodeAndEdgeUpdateIndex,
migration: migration_01_to_11.MigrateNodeAndEdgeUpdateIndex,
},
{
// The DB version that added the invoice event time
// series.
number: 2,
migration: migrateInvoiceTimeSeries,
migration: migration_01_to_11.MigrateInvoiceTimeSeries,
},
{
// The DB version that updated the embedded invoice in
// outgoing payments to match the new format.
number: 3,
migration: migrateInvoiceTimeSeriesOutgoingPayments,
migration: migration_01_to_11.MigrateInvoiceTimeSeriesOutgoingPayments,
},
{
// The version of the database where every channel
// always has two entries in the edges bucket. If
// a policy is unknown, this will be represented
// by a special byte sequence.
number: 4,
migration: migrateEdgePolicies,
migration: migration_01_to_11.MigrateEdgePolicies,
},
{
// The DB version where we persist each attempt to send
// an HTLC to a payment hash, and track whether the
// payment is in-flight, succeeded, or failed.
number: 5,
migration: paymentStatusesMigration,
migration: migration_01_to_11.PaymentStatusesMigration,
},
{
// The DB version that properly prunes stale entries
// from the edge update index.
number: 6,
migration: migratePruneEdgeUpdateIndex,
migration: migration_01_to_11.MigratePruneEdgeUpdateIndex,
},
{
// The DB version that migrates the ChannelCloseSummary
// to a format where optional fields are indicated with
// boolean flags.
number: 7,
migration: migrateOptionalChannelCloseSummaryFields,
migration: migration_01_to_11.MigrateOptionalChannelCloseSummaryFields,
},
{
// The DB version that changes the gossiper's message
// store keys to account for the message's type and
// ShortChannelID.
number: 8,
migration: migrateGossipMessageStoreKeys,
migration: migration_01_to_11.MigrateGossipMessageStoreKeys,
},
{
// The DB version where the payments and payment
// statuses are moved to being stored in a combined
// bucket.
number: 9,
migration: migrateOutgoingPayments,
migration: migration_01_to_11.MigrateOutgoingPayments,
},
{
// The DB version where we started to store legacy
// payload information for all routes, as well as the
// optional TLV records.
number: 10,
migration: migrateRouteSerialization,
migration: migration_01_to_11.MigrateRouteSerialization,
},
{
// Add invoice htlc and cltv delta fields.
number: 11,
migration: migrateInvoices,
migration: migration_01_to_11.MigrateInvoices,
},
}

Expand Down Expand Up @@ -266,10 +267,6 @@ func createChannelDB(dbPath string) error {
return err
}

if _, err := tx.CreateBucket(paymentBucket); err != nil {
return err
}

if _, err := tx.CreateBucket(nodeInfoBucket); err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions channeldb/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package channeldb
import (
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
)

// log is a logger that is initialized with no output filters. This
Expand All @@ -25,4 +26,5 @@ func DisableLog() {
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
migration_01_to_11.UseLogger(logger)
}
221 changes: 221 additions & 0 deletions channeldb/migration_01_to_11/addr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package migration_01_to_11

import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"

"github.com/lightningnetwork/lnd/tor"
)

// addressType specifies the network protocol and version that should be used
// when connecting to a node at a particular address.
type addressType uint8

const (
// tcp4Addr denotes an IPv4 TCP address.
tcp4Addr addressType = 0

// tcp6Addr denotes an IPv6 TCP address.
tcp6Addr addressType = 1

// v2OnionAddr denotes a version 2 Tor onion service address.
v2OnionAddr addressType = 2

// v3OnionAddr denotes a version 3 Tor (prop224) onion service address.
v3OnionAddr addressType = 3
)

// encodeTCPAddr serializes a TCP address into its compact raw bytes
// representation.
func encodeTCPAddr(w io.Writer, addr *net.TCPAddr) error {
var (
addrType byte
ip []byte
)

if addr.IP.To4() != nil {
addrType = byte(tcp4Addr)
ip = addr.IP.To4()
} else {
addrType = byte(tcp6Addr)
ip = addr.IP.To16()
}

if ip == nil {
return fmt.Errorf("unable to encode IP %v", addr.IP)
}

if _, err := w.Write([]byte{addrType}); err != nil {
return err
}

if _, err := w.Write(ip); err != nil {
return err
}

var port [2]byte
byteOrder.PutUint16(port[:], uint16(addr.Port))
if _, err := w.Write(port[:]); err != nil {
return err
}

return nil
}

// encodeOnionAddr serializes an onion address into its compact raw bytes
// representation.
func encodeOnionAddr(w io.Writer, addr *tor.OnionAddr) error {
var suffixIndex int
hostLen := len(addr.OnionService)
switch hostLen {
case tor.V2Len:
if _, err := w.Write([]byte{byte(v2OnionAddr)}); err != nil {
return err
}
suffixIndex = tor.V2Len - tor.OnionSuffixLen
case tor.V3Len:
if _, err := w.Write([]byte{byte(v3OnionAddr)}); err != nil {
return err
}
suffixIndex = tor.V3Len - tor.OnionSuffixLen
default:
return errors.New("unknown onion service length")
}

suffix := addr.OnionService[suffixIndex:]
if suffix != tor.OnionSuffix {
return fmt.Errorf("invalid suffix \"%v\"", suffix)
}

host, err := tor.Base32Encoding.DecodeString(
addr.OnionService[:suffixIndex],
)
if err != nil {
return err
}

// Sanity check the decoded length.
switch {
case hostLen == tor.V2Len && len(host) != tor.V2DecodedLen:
return fmt.Errorf("onion service %v decoded to invalid host %x",
addr.OnionService, host)

case hostLen == tor.V3Len && len(host) != tor.V3DecodedLen:
return fmt.Errorf("onion service %v decoded to invalid host %x",
addr.OnionService, host)
}

if _, err := w.Write(host); err != nil {
return err
}

var port [2]byte
byteOrder.PutUint16(port[:], uint16(addr.Port))
if _, err := w.Write(port[:]); err != nil {
return err
}

return nil
}

// deserializeAddr reads the serialized raw representation of an address and
// deserializes it into the actual address. This allows us to avoid address
// resolution within the channeldb package.
func deserializeAddr(r io.Reader) (net.Addr, error) {
var addrType [1]byte
if _, err := r.Read(addrType[:]); err != nil {
return nil, err
}

var address net.Addr
switch addressType(addrType[0]) {
case tcp4Addr:
var ip [4]byte
if _, err := r.Read(ip[:]); err != nil {
return nil, err
}

var port [2]byte
if _, err := r.Read(port[:]); err != nil {
return nil, err
}

address = &net.TCPAddr{
IP: net.IP(ip[:]),
Port: int(binary.BigEndian.Uint16(port[:])),
}
case tcp6Addr:
var ip [16]byte
if _, err := r.Read(ip[:]); err != nil {
return nil, err
}

var port [2]byte
if _, err := r.Read(port[:]); err != nil {
return nil, err
}

address = &net.TCPAddr{
IP: net.IP(ip[:]),
Port: int(binary.BigEndian.Uint16(port[:])),
}
case v2OnionAddr:
var h [tor.V2DecodedLen]byte
if _, err := r.Read(h[:]); err != nil {
return nil, err
}

var p [2]byte
if _, err := r.Read(p[:]); err != nil {
return nil, err
}

onionService := tor.Base32Encoding.EncodeToString(h[:])
onionService += tor.OnionSuffix
port := int(binary.BigEndian.Uint16(p[:]))

address = &tor.OnionAddr{
OnionService: onionService,
Port: port,
}
case v3OnionAddr:
var h [tor.V3DecodedLen]byte
if _, err := r.Read(h[:]); err != nil {
return nil, err
}

var p [2]byte
if _, err := r.Read(p[:]); err != nil {
return nil, err
}

onionService := tor.Base32Encoding.EncodeToString(h[:])
onionService += tor.OnionSuffix
port := int(binary.BigEndian.Uint16(p[:]))

address = &tor.OnionAddr{
OnionService: onionService,
Port: port,
}
default:
return nil, ErrUnknownAddressType
}

return address, nil
}

// serializeAddr serializes an address into its raw bytes representation so that
// it can be deserialized without requiring address resolution.
func serializeAddr(w io.Writer, address net.Addr) error {
switch addr := address.(type) {
case *net.TCPAddr:
return encodeTCPAddr(w, addr)
case *tor.OnionAddr:
return encodeOnionAddr(w, addr)
default:
return ErrUnknownAddressType
}
}
Loading

0 comments on commit 38e313a

Please sign in to comment.