diff --git a/channeldb/db.go b/channeldb/db.go index 696f3f008..6377df63a 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -1249,3 +1249,50 @@ func getMigrationsToApply(versions []version, version uint32) ([]migration, []ui return migrations, migrationVersions } + +// fetchHistoricalChanBucket returns a the channel bucket for a given outpoint +// from the historical channel bucket. If the bucket does not exist, +// ErrNoHistoricalBucket is returned. +func fetchHistoricalChanBucket(tx *bbolt.Tx, + outPoint *wire.OutPoint) (*bbolt.Bucket, error) { + + // First fetch the top level bucket which stores all data related to + // historically stored channels. + historicalChanBucket := tx.Bucket(historicalChannelBucket) + if historicalChanBucket == nil { + return nil, ErrNoHistoricalBucket + } + + // With the bucket for the node and chain fetched, we can now go down + // another level, for the channel itself. + var chanPointBuf bytes.Buffer + if err := writeOutpoint(&chanPointBuf, outPoint); err != nil { + return nil, err + } + chanBucket := historicalChanBucket.Bucket(chanPointBuf.Bytes()) + if chanBucket == nil { + return nil, ErrChannelNotFound + } + + return chanBucket, nil +} + +// FetchHistoricalChannel fetches open channel data from the historical channel +// bucket. +func (d *DB) FetchHistoricalChannel(outPoint *wire.OutPoint) (*OpenChannel, error) { + var channel *OpenChannel + err := d.View(func(tx *bbolt.Tx) error { + chanBucket, err := fetchHistoricalChanBucket(tx, outPoint) + if err != nil { + return err + } + + channel, err = fetchOpenChannel(chanBucket, outPoint) + return err + }) + if err != nil { + return nil, err + } + + return channel, nil +} diff --git a/channeldb/db_test.go b/channeldb/db_test.go index 935a287ce..4d678303a 100644 --- a/channeldb/db_test.go +++ b/channeldb/db_test.go @@ -693,3 +693,57 @@ func TestFetchChannels(t *testing.T) { }) } } + +// TestFetchHistoricalChannel tests lookup of historical channels. +func TestFetchHistoricalChannel(t *testing.T) { + cdb, cleanUp, err := makeTestDB() + if err != nil { + t.Fatalf("unable to make test database: %v", err) + } + defer cleanUp() + + // Create a an open channel in the database. + channel := createTestChannel(t, cdb, openChannelOption()) + + // First, try to lookup a channel when the bucket does not + // exist. + _, err = cdb.FetchHistoricalChannel(&channel.FundingOutpoint) + if err != ErrNoHistoricalBucket { + t.Fatalf("expected no bucket, got: %v", err) + } + + // Close the channel so that it will be written to the historical + // bucket. The values provided in the channel close summary are the + // minimum required for this call to run without panicking. + if err := channel.CloseChannel(&ChannelCloseSummary{ + ChanPoint: channel.FundingOutpoint, + RemotePub: channel.IdentityPub, + SettledBalance: btcutil.Amount(500), + }); err != nil { + t.Fatalf("unexpected error closing channel: %v", err) + } + + histChannel, err := cdb.FetchHistoricalChannel(&channel.FundingOutpoint) + if err != nil { + t.Fatalf("unexepected error getting channel: %v", err) + } + + // Set the db on our channel to nil so that we can check that all other + // fields on the channel equal those on the historical channel. + channel.Db = nil + + if !reflect.DeepEqual(histChannel, channel) { + t.Fatalf("expected: %v, got: %v", channel, histChannel) + } + + // Create an outpoint that will not be in the db and look it up. + badOutpoint := &wire.OutPoint{ + Hash: channel.FundingOutpoint.Hash, + Index: channel.FundingOutpoint.Index + 1, + } + _, err = cdb.FetchHistoricalChannel(badOutpoint) + if err != ErrChannelNotFound { + t.Fatalf("expected chan not found, got: %v", err) + } + +} diff --git a/channeldb/error.go b/channeldb/error.go index e0e754522..b1364fb4b 100644 --- a/channeldb/error.go +++ b/channeldb/error.go @@ -10,6 +10,11 @@ var ( // created. ErrNoChanDBExists = fmt.Errorf("channel db has not yet been created") + // ErrNoHistoricalBucket is returned when the historical channel bucket + // not been created yet. + ErrNoHistoricalBucket = fmt.Errorf("historical channel bucket has " + + "not yet been created") + // ErrDBReversion is returned when detecting an attempt to revert to a // prior database version. ErrDBReversion = fmt.Errorf("channel db cannot revert to prior version")