Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2783 from cfromknecht/wtserver-db
watchtower/wtdb: add bbolt-backed tower database
- Loading branch information
Showing
15 changed files
with
2,043 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package wtdb | ||
|
||
import ( | ||
"io" | ||
|
||
"github.com/lightningnetwork/lnd/channeldb" | ||
"github.com/lightningnetwork/lnd/lnwallet" | ||
"github.com/lightningnetwork/lnd/watchtower/blob" | ||
"github.com/lightningnetwork/lnd/watchtower/wtpolicy" | ||
) | ||
|
||
// UnknownElementType is an alias for channeldb.UnknownElementType. | ||
type UnknownElementType = channeldb.UnknownElementType | ||
|
||
// ReadElement deserializes a single element from the provided io.Reader. | ||
func ReadElement(r io.Reader, element interface{}) error { | ||
err := channeldb.ReadElement(r, element) | ||
switch { | ||
|
||
// Known to channeldb codec. | ||
case err == nil: | ||
return nil | ||
|
||
// Fail if error is not UnknownElementType. | ||
case err != nil: | ||
if _, ok := err.(UnknownElementType); !ok { | ||
return err | ||
} | ||
} | ||
|
||
// Process any wtdb-specific extensions to the codec. | ||
switch e := element.(type) { | ||
|
||
case *SessionID: | ||
if _, err := io.ReadFull(r, e[:]); err != nil { | ||
return err | ||
} | ||
|
||
case *BreachHint: | ||
if _, err := io.ReadFull(r, e[:]); err != nil { | ||
return err | ||
} | ||
|
||
case *wtpolicy.Policy: | ||
var ( | ||
blobType uint16 | ||
sweepFeeRate uint64 | ||
) | ||
err := channeldb.ReadElements(r, | ||
&blobType, | ||
&e.MaxUpdates, | ||
&e.RewardBase, | ||
&e.RewardRate, | ||
&sweepFeeRate, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
e.BlobType = blob.Type(blobType) | ||
e.SweepFeeRate = lnwallet.SatPerKWeight(sweepFeeRate) | ||
|
||
// Type is still unknown to wtdb extensions, fail. | ||
default: | ||
return channeldb.NewUnknownElementType( | ||
"ReadElement", element, | ||
) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// WriteElement serializes a single element into the provided io.Writer. | ||
func WriteElement(w io.Writer, element interface{}) error { | ||
err := channeldb.WriteElement(w, element) | ||
switch { | ||
|
||
// Known to channeldb codec. | ||
case err == nil: | ||
return nil | ||
|
||
// Fail if error is not UnknownElementType. | ||
case err != nil: | ||
if _, ok := err.(UnknownElementType); !ok { | ||
return err | ||
} | ||
} | ||
|
||
// Process any wtdb-specific extensions to the codec. | ||
switch e := element.(type) { | ||
|
||
case SessionID: | ||
if _, err := w.Write(e[:]); err != nil { | ||
return err | ||
} | ||
|
||
case BreachHint: | ||
if _, err := w.Write(e[:]); err != nil { | ||
return err | ||
} | ||
|
||
case wtpolicy.Policy: | ||
return channeldb.WriteElements(w, | ||
uint16(e.BlobType), | ||
e.MaxUpdates, | ||
e.RewardBase, | ||
e.RewardRate, | ||
uint64(e.SweepFeeRate), | ||
) | ||
|
||
// Type is still unknown to wtdb extensions, fail. | ||
default: | ||
return channeldb.NewUnknownElementType( | ||
"WriteElement", element, | ||
) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// WriteElements serializes a variadic list of elements into the given | ||
// io.Writer. | ||
func WriteElements(w io.Writer, elements ...interface{}) error { | ||
for _, element := range elements { | ||
if err := WriteElement(w, element); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// ReadElements deserializes the provided io.Reader into a variadic list of | ||
// target elements. | ||
func ReadElements(r io.Reader, elements ...interface{}) error { | ||
for _, element := range elements { | ||
if err := ReadElement(r, element); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package wtdb_test | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"reflect" | ||
"testing" | ||
"testing/quick" | ||
|
||
"github.com/lightningnetwork/lnd/watchtower/wtdb" | ||
) | ||
|
||
// dbObject is abstract object support encoding and decoding. | ||
type dbObject interface { | ||
Encode(io.Writer) error | ||
Decode(io.Reader) error | ||
} | ||
|
||
// TestCodec serializes and deserializes wtdb objects in order to test that that | ||
// the codec understands all of the required field types. The test also asserts | ||
// that decoding an object into another results in an equivalent object. | ||
func TestCodec(t *testing.T) { | ||
mainScenario := func(obj dbObject) bool { | ||
// Ensure encoding the object succeeds. | ||
var b bytes.Buffer | ||
err := obj.Encode(&b) | ||
if err != nil { | ||
t.Fatalf("unable to encode: %v", err) | ||
return false | ||
} | ||
|
||
var obj2 dbObject | ||
switch obj.(type) { | ||
case *wtdb.SessionInfo: | ||
obj2 = &wtdb.SessionInfo{} | ||
case *wtdb.SessionStateUpdate: | ||
obj2 = &wtdb.SessionStateUpdate{} | ||
default: | ||
t.Fatalf("unknown type: %T", obj) | ||
return false | ||
} | ||
|
||
// Ensure decoding the object succeeds. | ||
err = obj2.Decode(bytes.NewReader(b.Bytes())) | ||
if err != nil { | ||
t.Fatalf("unable to decode: %v", err) | ||
return false | ||
} | ||
|
||
// Assert the original and decoded object match. | ||
if !reflect.DeepEqual(obj, obj2) { | ||
t.Fatalf("encode/decode mismatch, want: %v, "+ | ||
"got: %v", obj, obj2) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
scenario interface{} | ||
}{ | ||
{ | ||
name: "SessionInfo", | ||
scenario: func(obj wtdb.SessionInfo) bool { | ||
return mainScenario(&obj) | ||
}, | ||
}, | ||
{ | ||
name: "SessionStateUpdate", | ||
scenario: func(obj wtdb.SessionStateUpdate) bool { | ||
return mainScenario(&obj) | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
if err := quick.Check(test.scenario, nil); err != nil { | ||
t.Fatalf("fuzz checks for msg=%s failed: %v", | ||
test.name, err) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package wtdb | ||
|
||
import ( | ||
"github.com/btcsuite/btclog" | ||
"github.com/lightningnetwork/lnd/build" | ||
) | ||
|
||
// log is a logger that is initialized with no output filters. This | ||
// means the package will not perform any logging by default until the caller | ||
// requests it. | ||
var log btclog.Logger | ||
|
||
// The default amount of logging is none. | ||
func init() { | ||
UseLogger(build.NewSubLogger("WTDB", nil)) | ||
} | ||
|
||
// DisableLog disables all library log output. Logging output is disabled | ||
// by default until UseLogger is called. | ||
func DisableLog() { | ||
UseLogger(btclog.Disabled) | ||
} | ||
|
||
// UseLogger uses a specified Logger to output package logging info. | ||
// This should be used in preference to SetLogWriter if the caller is also | ||
// using btclog. | ||
func UseLogger(logger btclog.Logger) { | ||
log = logger | ||
} | ||
|
||
// logClosure is used to provide a closure over expensive logging operations so | ||
// don't have to be performed when the logging level doesn't warrant it. | ||
type logClosure func() string | ||
|
||
// String invokes the underlying function and returns the result. | ||
func (c logClosure) String() string { | ||
return c() | ||
} | ||
|
||
// newLogClosure returns a new closure over a function that returns a string | ||
// which itself provides a Stringer interface so that it can be used with the | ||
// logging system. | ||
func newLogClosure(c func() string) logClosure { | ||
return logClosure(c) | ||
} |
Oops, something went wrong.