From f460060692ee218383c149b7650623b2d53dca9e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 26 Apr 2021 15:39:32 +0200 Subject: [PATCH] wasm --- .gitignore | 2 + Makefile | 3 + clientdb/account.go | 24 +- clientdb/batch.go | 50 +- clientdb/batch_snapshot.go | 77 ++- clientdb/batch_snapshot_test.go | 8 +- clientdb/batch_test.go | 10 +- clientdb/codec.go | 23 +- clientdb/codec_test.go | 24 +- clientdb/db.go | 69 +- clientdb/driver.go | 20 + clientdb/driver_js.go | 512 ++++++++++++++ clientdb/event.go | 12 +- clientdb/event_test.go | 10 +- clientdb/metadata.go | 65 +- .../migrations/migration_01_add_events.go | 12 +- clientdb/order.go | 40 +- clientdb/order_event.go | 16 +- clientdb/sidecar.go | 38 +- cmd/pool/debug.go | 3 +- cmd/pool/main.go | 7 +- config.go | 66 +- event/event.go | 4 +- go.mod | 15 +- go.sum | 133 ++-- index.html | 62 ++ lnd.go | 61 ++ lnd_js.go | 58 ++ macaroons.go | 2 + macaroons_js.go | 34 + rpcserver.go | 5 +- run.go | 28 +- server.go | 58 +- wasm_exec.js | 626 ++++++++++++++++++ 34 files changed, 1841 insertions(+), 336 deletions(-) create mode 100644 clientdb/driver.go create mode 100644 clientdb/driver_js.go create mode 100644 index.html create mode 100644 lnd.go create mode 100644 lnd_js.go create mode 100644 macaroons_js.go create mode 100644 wasm_exec.js diff --git a/.gitignore b/.gitignore index 4d8b574..f5f6e7b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /poold cmd/pool/pool cmd/poold/poold + +pool.wasm diff --git a/Makefile b/Makefile index d2e982b..eab6170 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,9 @@ release: $(VERSION_CHECK) ./scripts/release.sh build-release "$(VERSION_TAG)" "$(BUILD_SYSTEM)" "" "$(RELEASE_LDFLAGS)" +build-wasm: + GOOS=js GOARCH=wasm go build -trimpath -ldflags="$(LDFLAGS)" -tags=appengine -v -o pool.wasm $(PKG)/cmd/poold + scratch: build # ======= diff --git a/clientdb/account.go b/clientdb/account.go index 105691c..0eb360f 100644 --- a/clientdb/account.go +++ b/clientdb/account.go @@ -6,8 +6,8 @@ import ( "io" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" - "go.etcd.io/bbolt" ) var ( @@ -28,8 +28,8 @@ func getAccountKey(account *account.Account) []byte { // AddAccount adds a record for the account to the database. func (db *DB) AddAccount(account *account.Account) error { - return db.Update(func(tx *bbolt.Tx) error { - accounts, err := getBucket(tx, accountBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + accounts, err := getWriteBucket(tx, accountBucketKey) if err != nil { return err } @@ -43,8 +43,8 @@ func (db *DB) AddAccount(account *account.Account) error { func (db *DB) UpdateAccount(acct *account.Account, modifiers ...account.Modifier) error { - err := db.Update(func(tx *bbolt.Tx) error { - accounts, err := getBucket(tx, accountBucketKey) + err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + accounts, err := getWriteBucket(tx, accountBucketKey) if err != nil { return err } @@ -65,7 +65,7 @@ func (db *DB) UpdateAccount(acct *account.Account, // updateAccount reads an account from the src bucket, applies the given // modifiers to it, and store it back into dst bucket. -func updateAccount(src, dst *bbolt.Bucket, accountKey []byte, +func updateAccount(src, dst walletdb.ReadWriteBucket, accountKey []byte, modifiers []account.Modifier) (*account.Account, error) { dbAccount, err := readAccount(src, accountKey) @@ -84,8 +84,8 @@ func updateAccount(src, dst *bbolt.Bucket, accountKey []byte, // ErrAccountNotFound if it's not found. func (db *DB) Account(traderKey *btcec.PublicKey) (*account.Account, error) { var acct *account.Account - err := db.View(func(tx *bbolt.Tx) error { - accounts, err := getBucket(tx, accountBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + accounts, err := getReadBucket(tx, accountBucketKey) if err != nil { return err } @@ -105,8 +105,8 @@ func (db *DB) Account(traderKey *btcec.PublicKey) (*account.Account, error) { // Accounts retrieves all known accounts from the database. func (db *DB) Accounts() ([]*account.Account, error) { var res []*account.Account - err := db.View(func(tx *bbolt.Tx) error { - accounts, err := getBucket(tx, accountBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + accounts, err := getReadBucket(tx, accountBucketKey) if err != nil { return err } @@ -133,7 +133,7 @@ func (db *DB) Accounts() ([]*account.Account, error) { return res, nil } -func storeAccount(targetBucket *bbolt.Bucket, a *account.Account) error { +func storeAccount(targetBucket walletdb.ReadWriteBucket, a *account.Account) error { accountKey := getAccountKey(a) var accountBuf bytes.Buffer @@ -144,7 +144,7 @@ func storeAccount(targetBucket *bbolt.Bucket, a *account.Account) error { return targetBucket.Put(accountKey, accountBuf.Bytes()) } -func readAccount(sourceBucket *bbolt.Bucket, +func readAccount(sourceBucket walletdb.ReadBucket, accountKey []byte) (*account.Account, error) { accountBytes := sourceBucket.Get(accountKey) diff --git a/clientdb/batch.go b/clientdb/batch.go index a76fd94..c5e997b 100644 --- a/clientdb/batch.go +++ b/clientdb/batch.go @@ -3,9 +3,9 @@ package clientdb import ( "fmt" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/order" - "go.etcd.io/bbolt" ) var ( @@ -48,28 +48,28 @@ func (db *DB) StorePendingBatch(batch *order.Batch, orders []order.Nonce, } // Wrap the whole batch update in a single update transaction. - return db.Update(func(tx *bbolt.Tx) error { + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { // Before updating the set of orders and accounts, we'll first // delete the buckets containing any existing staged updates. // This is to done to handle the case where the first version of // a batch updated an order/account, but its second version // didn't. Without this, our state would become desynchronized // with the auction. - bucket, err := getBucket(tx, batchBucketKey) + bucket, err := getWriteBucket(tx, batchBucketKey) if err != nil { return err } - err = bucket.DeleteBucket(pendingBatchAccountsBucketKey) - if err != nil && err != bbolt.ErrBucketNotFound { + err = bucket.DeleteNestedBucket(pendingBatchAccountsBucketKey) + if err != nil && err != walletdb.ErrBucketNotFound { return err } - err = bucket.DeleteBucket(pendingBatchOrdersBucketKey) - if err != nil && err != bbolt.ErrBucketNotFound { + err = bucket.DeleteNestedBucket(pendingBatchOrdersBucketKey) + if err != nil && err != walletdb.ErrBucketNotFound { return err } // Update orders first. - ordersBucket, err := getBucket(tx, ordersBucketKey) + ordersBucket, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -94,7 +94,7 @@ func (db *DB) StorePendingBatch(batch *order.Batch, orders []order.Nonce, } // Then update the accounts. - accountsBucket, err := getBucket(tx, accountBucketKey) + accountsBucket, err := getWriteBucket(tx, accountBucketKey) if err != nil { return err } @@ -142,7 +142,7 @@ func (db *DB) StorePendingBatch(batch *order.Batch, orders []order.Nonce, // If there isn't one, account.ErrNoPendingBatch is returned. func (db *DB) PendingBatchSnapshot() (*LocalBatchSnapshot, error) { var batchSnapshot *LocalBatchSnapshot - err := db.View(func(tx *bbolt.Tx) error { + err := walletdb.View(db, func(tx walletdb.ReadTx) error { var err error batchSnapshot, err = fetchPendingBatchSnapshot(tx) return err @@ -152,8 +152,8 @@ func (db *DB) PendingBatchSnapshot() (*LocalBatchSnapshot, error) { // pendingBatchID retrieves the stored pending batch ID within a database // transaction. -func pendingBatchID(tx *bbolt.Tx) (order.BatchID, error) { - bucket, err := getBucket(tx, batchBucketKey) +func pendingBatchID(tx walletdb.ReadTx) (order.BatchID, error) { + bucket, err := getReadBucket(tx, batchBucketKey) if err != nil { return zeroBatchID, err } @@ -172,8 +172,8 @@ func pendingBatchID(tx *bbolt.Tx) (order.BatchID, error) { // without applying its staged updates to accounts and orders. If no pending // batch exists, this acts as a no-op. func (db *DB) DeletePendingBatch() error { - return db.Update(func(tx *bbolt.Tx) error { - bucket, err := getBucket(tx, batchBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + bucket, err := getWriteBucket(tx, batchBucketKey) if err != nil { return err } @@ -181,12 +181,12 @@ func (db *DB) DeletePendingBatch() error { if err := bucket.Delete(pendingBatchIDKey); err != nil { return err } - err = bucket.DeleteBucket(pendingBatchAccountsBucketKey) - if err != nil && err != bbolt.ErrBucketNotFound { + err = bucket.DeleteNestedBucket(pendingBatchAccountsBucketKey) + if err != nil && err != walletdb.ErrBucketNotFound { return err } - err = bucket.DeleteBucket(pendingBatchOrdersBucketKey) - if err != nil && err != bbolt.ErrBucketNotFound { + err = bucket.DeleteNestedBucket(pendingBatchOrdersBucketKey) + if err != nil && err != walletdb.ErrBucketNotFound { return err } @@ -200,7 +200,7 @@ func (db *DB) DeletePendingBatch() error { // modifications necessary, and allowing a trader to participate in a new batch. // If a pending batch is not found, account.ErrNoPendingBatch is returned. func (db *DB) MarkBatchComplete() error { - return db.Update(func(tx *bbolt.Tx) error { + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { pendingID, err := pendingBatchID(tx) if err != nil { return err @@ -215,8 +215,8 @@ func (db *DB) MarkBatchComplete() error { // applyBatchUpdates applies the staged updates for any accounts and orders that // participated in the latest pending batch. -func applyBatchUpdates(tx *bbolt.Tx) error { - bucket, err := getBucket(tx, batchBucketKey) +func applyBatchUpdates(tx walletdb.ReadWriteTx) error { + bucket, err := getWriteBucket(tx, batchBucketKey) if err != nil { return err } @@ -230,7 +230,7 @@ func applyBatchUpdates(tx *bbolt.Tx) error { if err != nil { return err } - accounts, err := getBucket(tx, accountBucketKey) + accounts, err := getWriteBucket(tx, accountBucketKey) if err != nil { return err } @@ -248,7 +248,7 @@ func applyBatchUpdates(tx *bbolt.Tx) error { // Once we've updated all of the accounts, we can remove the pending // updates as they've been committed. - if err := bucket.DeleteBucket(pendingBatchAccountsBucketKey); err != nil { + if err := bucket.DeleteNestedBucket(pendingBatchAccountsBucketKey); err != nil { return err } @@ -259,7 +259,7 @@ func applyBatchUpdates(tx *bbolt.Tx) error { if err != nil { return err } - orders, err := getBucket(tx, ordersBucketKey) + orders, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -278,7 +278,7 @@ func applyBatchUpdates(tx *bbolt.Tx) error { // Once again, remove the pending order updates as they've been // committed. - if err := bucket.DeleteBucket(pendingBatchOrdersBucketKey); err != nil { + if err := bucket.DeleteNestedBucket(pendingBatchOrdersBucketKey); err != nil { return err } diff --git a/clientdb/batch_snapshot.go b/clientdb/batch_snapshot.go index 57f1317..86d4b80 100644 --- a/clientdb/batch_snapshot.go +++ b/clientdb/batch_snapshot.go @@ -7,11 +7,11 @@ import ( "io" "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/terms" "github.com/lightningnetwork/lnd/lnwallet/chainfee" - "go.etcd.io/bbolt" ) // batch-snapshot-bucket @@ -137,7 +137,7 @@ func NewSnapshot(batch *order.Batch, ourOrders []order.Order, // participated in. func (db *DB) GetLocalBatchSnapshots() ([]*LocalBatchSnapshot, error) { var snapshots []*LocalBatchSnapshot - err := db.View(func(tx *bbolt.Tx) error { + err := walletdb.View(db, func(tx walletdb.ReadTx) error { var err error snapshots, err = db.fetchLocalBatchSnapshots(tx) return err @@ -154,8 +154,8 @@ func (db *DB) GetLocalBatchSnapshot(id order.BatchID) ( *LocalBatchSnapshot, error) { var snapshot *LocalBatchSnapshot - err := db.View(func(tx *bbolt.Tx) error { - _, seqBucket, indexBucket, err := getSnapshotBuckets(tx) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + _, seqBucket, indexBucket, err := getSnapshotReadBuckets(tx) if err != nil { return err } @@ -165,7 +165,7 @@ func (db *DB) GetLocalBatchSnapshot(id order.BatchID) ( return fmt.Errorf("snapshot of batch %x not found", id[:]) } - rootOrderBucket, err := getBucket(tx, ordersBucketKey) + rootOrderBucket, err := getReadBucket(tx, ordersBucketKey) if err != nil { return err } @@ -182,14 +182,14 @@ func (db *DB) GetLocalBatchSnapshot(id order.BatchID) ( return snapshot, nil } -func (db *DB) fetchLocalBatchSnapshots(tx *bbolt.Tx) ([]*LocalBatchSnapshot, - error) { +func (db *DB) fetchLocalBatchSnapshots( + tx walletdb.ReadTx) ([]*LocalBatchSnapshot, error) { - _, seqBucket, _, err := getSnapshotBuckets(tx) + _, seqBucket, _, err := getSnapshotReadBuckets(tx) if err != nil { return nil, err } - rootOrderBucket, err := getBucket(tx, ordersBucketKey) + rootOrderBucket, err := getReadBucket(tx, ordersBucketKey) if err != nil { return nil, err } @@ -216,10 +216,10 @@ func (db *DB) fetchLocalBatchSnapshots(tx *bbolt.Tx) ([]*LocalBatchSnapshot, return snapshots, nil } -func fetchLocalBatchSnapshot(seqBucket *bbolt.Bucket, seqNum []byte, - rootOrderBucket *bbolt.Bucket) (*LocalBatchSnapshot, error) { +func fetchLocalBatchSnapshot(seqBucket walletdb.ReadBucket, seqNum []byte, + rootOrderBucket walletdb.ReadBucket) (*LocalBatchSnapshot, error) { - snapshotBucket, err := getNestedBucket(seqBucket, seqNum, false) + snapshotBucket, err := getNestedReadBucket(seqBucket, seqNum) if err != nil { return nil, err } @@ -241,8 +241,8 @@ func fetchLocalBatchSnapshot(seqBucket *bbolt.Bucket, seqNum []byte, // The snapshot doesn't contain all the order information needed, so // we'll retrieve the missing data. for nonce, o := range batchSnapshot.Orders { - orderBucket, err := getNestedBucket( - rootOrderBucket, nonce[:], false, + orderBucket, err := getNestedReadBucket( + rootOrderBucket, nonce[:], ) if err != nil { return nil, ErrNoOrder @@ -299,10 +299,10 @@ func fetchLocalBatchSnapshot(seqBucket *bbolt.Bucket, seqNum []byte, return batchSnapshot, nil } -func storePendingBatchSnapshot(tx *bbolt.Tx, +func storePendingBatchSnapshot(tx walletdb.ReadWriteTx, snapshot *LocalBatchSnapshot) error { - topBucket, err := getBucket(tx, batchSnapshotBucketKey) + topBucket, err := getWriteBucket(tx, batchSnapshotBucketKey) if err != nil { return err } @@ -319,8 +319,8 @@ func storePendingBatchSnapshot(tx *bbolt.Tx, // fetchPendingBatchSnapshot retrieves the currently pending batch snapshot from // the database or returns the account.ErrNoPendingBatch error if none exists. -func fetchPendingBatchSnapshot(tx *bbolt.Tx) (*LocalBatchSnapshot, error) { - topBucket, err := getBucket(tx, batchSnapshotBucketKey) +func fetchPendingBatchSnapshot(tx walletdb.ReadTx) (*LocalBatchSnapshot, error) { + topBucket, err := getReadBucket(tx, batchSnapshotBucketKey) if err != nil { return nil, err } @@ -335,8 +335,8 @@ func fetchPendingBatchSnapshot(tx *bbolt.Tx) (*LocalBatchSnapshot, error) { // finalizeBatchSnapshot moves the pending batch snapshot into the sub-bucket // indexed by sequence numbers. -func finalizeBatchSnapshot(tx *bbolt.Tx, batchID order.BatchID) error { - topBucket, seqBucket, indexBucket, err := getSnapshotBuckets(tx) +func finalizeBatchSnapshot(tx walletdb.ReadWriteTx, batchID order.BatchID) error { + topBucket, seqBucket, indexBucket, err := getSnapshotWriteBuckets(tx) if err != nil { return err } @@ -379,10 +379,37 @@ func finalizeBatchSnapshot(tx *bbolt.Tx, batchID order.BatchID) error { return nil } -func getSnapshotBuckets(tx *bbolt.Tx) (*bbolt.Bucket, *bbolt.Bucket, - *bbolt.Bucket, error) { +func getSnapshotReadBuckets(tx walletdb.ReadTx) (walletdb.ReadBucket, + walletdb.ReadBucket, walletdb.ReadBucket, error) { - topBucket, err := getBucket(tx, batchSnapshotBucketKey) + topBucket, err := getReadBucket(tx, batchSnapshotBucketKey) + if err != nil { + return nil, nil, nil, err + } + + // Get the sub-buckets. We expect them to be created at DB init, so we + // don't attempt to create them if non-existent. + seqBucket, err := getNestedReadBucket( + topBucket, batchSnapshotSeqBucketKey, + ) + if err != nil { + return nil, nil, nil, err + } + + indexBucket, err := getNestedReadBucket( + topBucket, batchSnapshotBatchIDIndexBucketKey, + ) + if err != nil { + return nil, nil, nil, err + } + + return topBucket, seqBucket, indexBucket, nil +} + +func getSnapshotWriteBuckets(tx walletdb.ReadWriteTx) (walletdb.ReadWriteBucket, + walletdb.ReadWriteBucket, walletdb.ReadWriteBucket, error) { + + topBucket, err := getWriteBucket(tx, batchSnapshotBucketKey) if err != nil { return nil, nil, nil, err } @@ -695,8 +722,8 @@ func deserializeMatchedOrder(r io.Reader) (order.Nonce, } // deletePendingSnapshot deletes the pending batch snapshot. -func deletePendingSnapshot(tx *bbolt.Tx) error { - topBucket, err := getBucket(tx, batchSnapshotBucketKey) +func deletePendingSnapshot(tx walletdb.ReadWriteTx) error { + topBucket, err := getWriteBucket(tx, batchSnapshotBucketKey) if err != nil { return err } diff --git a/clientdb/batch_snapshot_test.go b/clientdb/batch_snapshot_test.go index 9d4c004..d9c2482 100644 --- a/clientdb/batch_snapshot_test.go +++ b/clientdb/batch_snapshot_test.go @@ -8,12 +8,12 @@ import ( "testing" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/walletdb" "github.com/davecgh/go-spew/spew" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/terms" "github.com/stretchr/testify/require" - "go.etcd.io/bbolt" ) var ( @@ -207,7 +207,7 @@ func TestGetLocalBatchSnapshots(t *testing.T) { // Store the same batch 10 times, only changing the batch ID each time. for i := 0; i < 10; i++ { i := i - err := store.Update(func(tx *bbolt.Tx) error { + err := walletdb.Update(store, func(tx walletdb.ReadWriteTx) error { testSnapshot.BatchID[0] = byte(i) err := storePendingBatchSnapshot(tx, testSnapshot) if err != nil { @@ -235,14 +235,14 @@ func TestGetLocalBatchSnapshots(t *testing.T) { // Store and delete a pending snapshot, and make sure all other batches are // still there. testSnapshot.BatchID[0] = byte(99) - err = store.Update(func(tx *bbolt.Tx) error { + err = walletdb.Update(store, func(tx walletdb.ReadWriteTx) error { return storePendingBatchSnapshot(tx, testSnapshot) }) if err != nil { t.Fatal(err) } - err = store.Update(deletePendingSnapshot) + err = walletdb.Update(store, deletePendingSnapshot) if err != nil { t.Fatal(err) } diff --git a/clientdb/batch_test.go b/clientdb/batch_test.go index b085765..2801ca9 100644 --- a/clientdb/batch_test.go +++ b/clientdb/batch_test.go @@ -8,11 +8,11 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/terms" "github.com/lightningnetwork/lnd/keychain" - "go.etcd.io/bbolt" ) var ( @@ -396,18 +396,18 @@ func TestDeletePendingBatch(t *testing.T) { assertPendingBatch := func(exists bool) { t.Helper() - err := db.View(func(tx *bbolt.Tx) error { - root := tx.Bucket(batchBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + root := tx.ReadBucket(batchBucketKey) if (root.Get(pendingBatchIDKey) != nil) != exists { return fmt.Errorf("found unexpected key %v", string(pendingBatchIDKey)) } - if (root.Bucket(pendingBatchAccountsBucketKey) != nil) != exists { + if (root.NestedReadBucket(pendingBatchAccountsBucketKey) != nil) != exists { return fmt.Errorf("found unexpected bucket %v", string(pendingBatchAccountsBucketKey)) } - if (root.Bucket(pendingBatchOrdersBucketKey) != nil) != exists { + if (root.NestedReadBucket(pendingBatchOrdersBucketKey) != nil) != exists { return fmt.Errorf("found unexpected bucket %v", string(pendingBatchOrdersBucketKey)) } diff --git a/clientdb/codec.go b/clientdb/codec.go index 1cc6829..39c5e4a 100644 --- a/clientdb/codec.go +++ b/clientdb/codec.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/terms" @@ -17,7 +18,6 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" - "go.etcd.io/bbolt" ) var ( @@ -272,8 +272,8 @@ func ReadElement(r io.Reader, element interface{}) error { // nolint:gocyclo // getAdditionalDataBucket returns the bucket for additional data of a database // object located at mainKey. -func getAdditionalDataBucket(parentBucket *bbolt.Bucket, - mainKey []byte, createBucket bool) (*bbolt.Bucket, error) { +func getAdditionalDataBucket(parentBucket walletdb.ReadWriteBucket, + mainKey []byte, createBucket bool) (walletdb.ReadWriteBucket, error) { keyLen := len(mainKey) + len(additionalDataSuffix) additionalDataKey := make([]byte, keyLen) @@ -286,10 +286,21 @@ func getAdditionalDataBucket(parentBucket *bbolt.Bucket, return parentBucket.CreateBucketIfNotExists(additionalDataKey) } - return parentBucket.Bucket(additionalDataKey), nil + return parentBucket.NestedReadWriteBucket(additionalDataKey), nil } -func writeAdditionalValue(targetBucket *bbolt.Bucket, valueKey []byte, +func getAdditionalDataReadBucket(parentBucket walletdb.ReadBucket, + mainKey []byte) (walletdb.ReadBucket, error) { + + keyLen := len(mainKey) + len(additionalDataSuffix) + additionalDataKey := make([]byte, keyLen) + copy(additionalDataKey, mainKey) + copy(additionalDataKey[len(mainKey):], additionalDataSuffix) + + return parentBucket.NestedReadBucket(additionalDataKey), nil +} + +func writeAdditionalValue(targetBucket walletdb.ReadWriteBucket, valueKey []byte, value interface{}) error { var valueBuf bytes.Buffer @@ -300,7 +311,7 @@ func writeAdditionalValue(targetBucket *bbolt.Bucket, valueKey []byte, return targetBucket.Put(valueKey, valueBuf.Bytes()) } -func readAdditionalValue(sourceBucket *bbolt.Bucket, valueKey []byte, +func readAdditionalValue(sourceBucket walletdb.ReadBucket, valueKey []byte, valueTarget interface{}, defaultValue interface{}) error { // First of all, check that the valueTarget is a pointer to the same diff --git a/clientdb/codec_test.go b/clientdb/codec_test.go index 251a09a..d834393 100644 --- a/clientdb/codec_test.go +++ b/clientdb/codec_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/account" "github.com/stretchr/testify/require" - "go.etcd.io/bbolt" ) var ( @@ -48,9 +48,9 @@ func TestAdditionalData(t *testing.T) { // Now try to read the additional value that does not exist yet. We // should instead get back the default value. myAdditionalValue := uint32(0) - require.NoError(t, db.DB.View(func(tx *bbolt.Tx) error { - subBucket, err := getAdditionalDataBucket( - tx.Bucket(accountBucketKey), accountKey, false, + require.NoError(t, walletdb.View(db, func(tx walletdb.ReadTx) error { + subBucket, err := getAdditionalDataReadBucket( + tx.ReadBucket(accountBucketKey), accountKey, ) if err != nil { return err @@ -63,9 +63,9 @@ func TestAdditionalData(t *testing.T) { require.Equal(t, additionalDataTestDefaultValue, myAdditionalValue) // Write the additional into the sub bucket of the account now. - require.NoError(t, db.DB.Update(func(tx *bbolt.Tx) error { + require.NoError(t, walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { subBucket, err := getAdditionalDataBucket( - tx.Bucket(accountBucketKey), accountKey, true, + tx.ReadWriteBucket(accountBucketKey), accountKey, true, ) if err != nil { return err @@ -78,9 +78,9 @@ func TestAdditionalData(t *testing.T) { // Read the additional info again, now we shouldn't get the default // value anymore. - require.NoError(t, db.DB.View(func(tx *bbolt.Tx) error { - subBucket, err := getAdditionalDataBucket( - tx.Bucket(accountBucketKey), accountKey, false, + require.NoError(t, walletdb.View(db, func(tx walletdb.ReadTx) error { + subBucket, err := getAdditionalDataReadBucket( + tx.ReadBucket(accountBucketKey), accountKey, ) if err != nil { return err @@ -96,9 +96,9 @@ func TestAdditionalData(t *testing.T) { // accidentally when using the readAdditionalValue function. // Read the additional info again, now we shouldn't get the default // value anymore. - require.Error(t, db.DB.View(func(tx *bbolt.Tx) error { - subBucket, err := getAdditionalDataBucket( - tx.Bucket(accountBucketKey), accountKey, false, + require.Error(t, walletdb.View(db, func(tx walletdb.ReadTx) error { + subBucket, err := getAdditionalDataReadBucket( + tx.ReadBucket(accountBucketKey), accountKey, ) if err != nil { return err diff --git a/clientdb/db.go b/clientdb/db.go index 8b40f2b..f0c2626 100644 --- a/clientdb/db.go +++ b/clientdb/db.go @@ -2,22 +2,18 @@ package clientdb import ( "encoding/binary" - "fmt" "os" "path/filepath" "time" - "go.etcd.io/bbolt" + "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/lnrpc" ) const ( // DBFilename is the default filename of the client database. DBFilename = "pool.db" - // dbFilePermission is the default permission the client database file - // is created with. - dbFilePermission = 0600 - // DefaultPoolDBTimeout is the default maximum time we wait for the // Pool bbolt database to be opened. If the database is already opened // by another process, the unique lock cannot be obtained. With the @@ -34,23 +30,22 @@ var ( // DB is a bolt-backed persistent store. type DB struct { - *bbolt.DB + walletdb.DB } // New creates a new bolt database that can be found at the given directory. func New(dir, fileName string) (*DB, error) { - firstInit := false path := filepath.Join(dir, fileName) - // If the database file does not exist yet, create its directory. - if !fileExists(path) { - if err := os.MkdirAll(dir, 0700); err != nil { - return nil, err - } - firstInit = true + // Detect first init of database for JS. + dbExists, err := isExistingDB(dir, path) + if err != nil { + return nil, err } - db, err := initDB(path, firstInit) + log.Debugf("Database %s exists: %v", path, dbExists) + + db, err := initDB(path, !dbExists) if err != nil { return nil, err } @@ -75,24 +70,26 @@ func fileExists(path string) bool { } // initDB initializes all of the required top-level buckets for the database. -func initDB(filepath string, firstInit bool) (*bbolt.DB, error) { - db, err := bbolt.Open(filepath, dbFilePermission, &bbolt.Options{ - Timeout: DefaultPoolDBTimeout, - }) - if err == bbolt.ErrTimeout { - return nil, fmt.Errorf("error while trying to open %s: timed "+ - "out after %v when trying to obtain exclusive lock - "+ - "make sure no other pool daemon process (standalone "+ - "or embedded in lightning-terminal) is running", - filepath, DefaultPoolDBTimeout) - } - if err != nil { - return nil, err +func initDB(filepath string, firstInit bool) (walletdb.DB, error) { + var ( + db walletdb.DB + err error + ) + if !lnrpc.FileExists(filepath) { + db, err = walletdb.Create(dbDriver, filepath, false, time.Second) + if err != nil { + return nil, err + } + } else { + db, err = walletdb.Open(dbDriver, filepath, false, time.Second) + if err != nil { + return nil, err + } } - err = db.Update(func(tx *bbolt.Tx) error { + err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { if firstInit { - metadataBucket, err := tx.CreateBucketIfNotExists( + metadataBucket, err := tx.CreateTopLevelBucket( metadataBucketKey, ) if err != nil { @@ -107,27 +104,27 @@ func initDB(filepath string, firstInit bool) (*bbolt.DB, error) { } } - _, err = tx.CreateBucketIfNotExists(accountBucketKey) + _, err = tx.CreateTopLevelBucket(accountBucketKey) if err != nil { return err } - _, err = tx.CreateBucketIfNotExists(ordersBucketKey) + _, err = tx.CreateTopLevelBucket(ordersBucketKey) if err != nil { return err } - _, err = tx.CreateBucketIfNotExists(sidecarsBucketKey) + _, err = tx.CreateTopLevelBucket(sidecarsBucketKey) if err != nil { return err } - _, err = tx.CreateBucketIfNotExists(batchBucketKey) + _, err = tx.CreateTopLevelBucket(batchBucketKey) if err != nil { return err } - _, err = tx.CreateBucketIfNotExists(eventBucketKey) + _, err = tx.CreateTopLevelBucket(eventBucketKey) if err != nil { return err } - snapshotBucket, err := tx.CreateBucketIfNotExists( + snapshotBucket, err := tx.CreateTopLevelBucket( batchSnapshotBucketKey, ) if err != nil { diff --git a/clientdb/driver.go b/clientdb/driver.go new file mode 100644 index 0000000..e077691 --- /dev/null +++ b/clientdb/driver.go @@ -0,0 +1,20 @@ +// +build !wasm + +package clientdb + +const ( + dbDriver = "bdb" +) + +func isExistingDB(dir, path string) (bool, error) { + // If the database file does not exist yet, create its directory. + if !fileExists(path) { + if err := os.MkdirAll(dir, 0700); err != nil { + return false, err + } + + return false, nil + } + + return true, nil +} diff --git a/clientdb/driver_js.go b/clientdb/driver_js.go new file mode 100644 index 0000000..76989ad --- /dev/null +++ b/clientdb/driver_js.go @@ -0,0 +1,512 @@ +package clientdb + +import ( + "encoding/base64" + "errors" + "fmt" + "io" + "strings" + "syscall/js" + + "github.com/btcsuite/btcwallet/walletdb" +) + +const ( + dbDriver = "localStorage" +) + +var ( + errNotSupported = fmt.Errorf("not supported") +) + +func init() { + // Register the driver. + driver := walletdb.Driver{ + DbType: dbDriver, + Create: openDB, + Open: openDB, + } + if err := walletdb.RegisterDriver(driver); err != nil { + panic(fmt.Sprintf("Failed to regiser database driver '%s': %v", + dbDriver, err)) + } +} + +func isExistingDB(_, _ string) (bool, error) { + ok := DetectStorage() + if !ok { + return false, ErrLocalStorageNotSupported + } + + _, err := getDBVersion(&transaction{path: string(metadataBucketKey)}) + if err == ErrDBVersionNotFound { + return false, nil + } else if err != nil { + return false, err + } + + return true, nil +} + +// openDB opens the database at the provided path. walletdb.ErrDbDoesNotExist +// is returned if the database doesn't exist and the create flag is not set. +func openDB(...interface{}) (walletdb.DB, error) { + ok := DetectStorage() + if !ok { + return nil, ErrLocalStorageNotSupported + } + + return &db{}, nil +} + +type transaction struct { + path string +} + +func (tx *transaction) subKey(key []byte) string { + return strings.Join([]string{tx.path, string(key)}, "/") +} + +func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket { + log.Debugf("ReadBucket(%s)", string(key)) + return tx.ReadWriteBucket(key) +} + +func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket { + log.Debugf("ReadWriteBucket(%s)", string(key)) + return &transaction{path: string(key)} +} + +func (tx *transaction) CreateTopLevelBucket( + key []byte) (walletdb.ReadWriteBucket, error) { + + return tx.ReadWriteBucket(key), nil +} + +func (tx *transaction) DeleteTopLevelBucket([]byte) error { + return fmt.Errorf("not supported") +} + +// Commit commits all changes that have been made through the root bucket and +// all of its sub-buckets to persistent storage. +// +// This function is part of the walletdb.ReadWriteTx interface implementation. +func (tx *transaction) Commit() error { + return nil +} + +// Rollback undoes all changes that have been made to the root bucket and all of +// its sub-buckets. +// +// This function is part of the walletdb.ReadTx interface implementation. +func (tx *transaction) Rollback() error { + return nil +} + +// OnCommit takes a function closure that will be executed when the transaction +// successfully gets committed. +// +// This function is part of the walletdb.ReadWriteTx interface implementation. +func (tx *transaction) OnCommit(f func()) { + f() +} + +// NestedReadWriteBucket retrieves a nested bucket with the given key. Returns +// nil if the bucket does not exist. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket { + log.Debugf("NestedReadWriteBucket(%s)", string(key)) + return &transaction{path: tx.subKey(key)} +} + +func (tx *transaction) NestedReadBucket(key []byte) walletdb.ReadBucket { + log.Debugf("NestedReadBucket(%s)", string(key)) + return tx.NestedReadWriteBucket(key) +} + +// CreateBucket creates and returns a new nested bucket with the given key. +// Returns ErrBucketExists if the bucket already exists, ErrBucketNameRequired +// if the key is empty, or ErrIncompatibleValue if the key value is otherwise +// invalid. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) CreateBucket(key []byte) (walletdb.ReadWriteBucket, error) { + log.Debugf("CreateBucket(%s)", string(key)) + return tx.NestedReadWriteBucket(key), nil +} + +// CreateBucketIfNotExists creates and returns a new nested bucket with the +// given key if it does not already exist. Returns ErrBucketNameRequired if the +// key is empty or ErrIncompatibleValue if the key value is otherwise invalid. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) CreateBucketIfNotExists( + key []byte) (walletdb.ReadWriteBucket, error) { + + log.Debugf("CreateBucketIfNotExists(%s)", string(key)) + return tx.NestedReadWriteBucket(key), nil +} + +// DeleteNestedBucket removes a nested bucket with the given key. Returns +// ErrTxNotWritable if attempted against a read-only transaction and +// ErrBucketNotFound if the specified bucket does not exist. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) DeleteNestedBucket([]byte) error { + return errNotSupported +} + +// ForEach invokes the passed function with every key/value pair in the bucket. +// This includes nested buckets, in which case the value is nil, but it does not +// include the key/value pairs within those nested buckets. +// +// NOTE: The values returned by this function are only valid during a +// transaction. Attempting to access them after a transaction has ended will +// likely result in an access violation. +// +// This function is part of the walletdb.ReadBucket interface implementation. +func (tx *transaction) ForEach(fn func(k, v []byte) error) error { + return ForEach(tx.path, func(key, valueBase64 string) error { + value, err := base64.StdEncoding.DecodeString(valueBase64) + if err != nil { + return err + } + + return fn([]byte(key), value) + }) +} + +func (tx *transaction) ForEachBucket(fn func(k []byte) error) error { + return errNotSupported +} + +// Put saves the specified key/value pair to the bucket. Keys that do not +// already exist are added and keys that already exist are overwritten. Returns +// ErrTxNotWritable if attempted against a read-only transaction. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) Put(key, value []byte) error { + fullKey := tx.subKey(key) + log.Debugf("Put(%s, %s)", fullKey, string(value)) + return SetItem(fullKey, base64.StdEncoding.EncodeToString(value)) +} + +// Get returns the value for the given key. Returns nil if the key does +// not exist in this bucket (or nested buckets). +// +// NOTE: The value returned by this function is only valid during a +// transaction. Attempting to access it after a transaction has ended +// will likely result in an access violation. +// +// This function is part of the walletdb.ReadBucket interface implementation. +func (tx *transaction) Get(key []byte) []byte { + fullKey := tx.subKey(key) + log.Debugf("Get(%s)", fullKey) + valueBase64, err := GetItem(fullKey) + if err != nil { + return nil + } + + value, err := base64.StdEncoding.DecodeString(valueBase64) + if err != nil { + return nil + } + return value +} + +// Delete removes the specified key from the bucket. Deleting a key that does +// not exist does not return an error. Returns ErrTxNotWritable if attempted +// against a read-only transaction. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) Delete(key []byte) error { + fullKey := tx.subKey(key) + log.Debugf("Delete(%s)", fullKey) + return RemoveItem(fullKey) +} + +func (tx *transaction) ReadCursor() walletdb.ReadCursor { + return nil +} + +// ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's +// key/value pairs and nested buckets in forward or backward order. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) ReadWriteCursor() walletdb.ReadWriteCursor { + return nil +} + +// Tx returns the bucket's transaction. +// +// This function is part of the walletdb.ReadWriteBucket interface implementation. +func (tx *transaction) Tx() walletdb.ReadWriteTx { + return tx +} + +// NextSequence returns an autoincrementing integer for the bucket. +func (tx *transaction) NextSequence() (uint64, error) { + return 0, errNotSupported +} + +// SetSequence updates the sequence number for the bucket. +func (tx *transaction) SetSequence(uint64) error { + return errNotSupported +} + +// Sequence returns the current integer for the bucket without incrementing it. +func (tx *transaction) Sequence() uint64 { + return 0 +} + +// db represents a collection of namespaces which are persisted and implements +// the walletdb.Db interface. All database access is performed through +// transactions which are obtained through the specific Namespace. +type db struct { +} + +// Enforce db implements the walletdb.Db interface. +var _ walletdb.DB = (*db)(nil) + +func (db *db) beginTx() (*transaction, error) { + return &transaction{}, nil +} + +func (db *db) BeginReadTx() (walletdb.ReadTx, error) { + return db.beginTx() +} + +func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) { + return db.beginTx() +} + +// Copy writes a copy of the database to the provided writer. This call will +// start a read-only transaction to perform all operations. +// +// This function is part of the walletdb.Db interface implementation. +func (db *db) Copy(io.Writer) error { + return errNotSupported +} + +// Close cleanly shuts down the database and syncs all data. +// +// This function is part of the walletdb.Db interface implementation. +func (db *db) Close() error { + return nil +} + +// Batch is similar to the package-level Update method, but it will attempt to +// optismitcally combine the invocation of several transaction functions into a +// single db write transaction. +// +// This function is part of the walletdb.Db interface implementation. +func (db *db) Batch(f func(tx walletdb.ReadWriteTx) error) error { + tx, err := db.BeginReadWriteTx() + if err != nil { + return err + } + + return f(tx) +} + +var localStorage js.Value + +// ErrLocalStorageNotSupported is returned if localStorage is not supported. +var ErrLocalStorageNotSupported = errors.New("localStorage does not appear " + + "to be supported/enabled in this browser") + +// ItemNotFoundError is returned if an item with the given key does not exist in +// localStorage. +type ItemNotFoundError struct { + msg string +} + +// Error implements the error interface. +func (e ItemNotFoundError) Error() string { + return e.msg +} + +func newItemNotFoundError(format string, args ...interface{}) ItemNotFoundError { + return ItemNotFoundError{ + msg: fmt.Sprintf(format, args...), + } +} + +// DetectStorage detects and (re)initializes the localStorage. +func DetectStorage() (ok bool) { + defer func() { + if r := recover(); r != nil { + log.Debugf("Recovered from panic in DetectStorage: %v", + r) + localStorage = js.Undefined() + ok = false + } + }() + + localStorage = js.Global().Get("localStorage") + + if localStorage.IsUndefined() { + log.Debugf("Value is undefined") + return false + } + + // Cf. https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API + // https://gist.github.com/paulirish/5558557 + x := "__storage_test__" + localStorage.Set(x, x) + obj := localStorage.Get(x) + if obj.IsUndefined() { + log.Debugf("Storage test return value nil") + localStorage = js.Undefined() + return false + } + localStorage.Call("removeItem", x) + return true +} + +// SetItem saves the given item in localStorage under the given key. +func SetItem(key, item string) (err error) { + if localStorage.IsUndefined() { + return ErrLocalStorageNotSupported + } + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("could not use local "+ + "storage: %v", r) + } + } + }() + + localStorage.Call("setItem", key, item) + return nil +} + +// GetItem finds and returns the item identified by key. If there is no item in +// localStorage with the given key, GetItem will return an ItemNotFoundError. +func GetItem(key string) (s string, err error) { + if localStorage.IsUndefined() { + return "", ErrLocalStorageNotSupported + } + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("could not use local "+ + "storage: %v", r) + } + s = "" + } + }() + + item := localStorage.Call("getItem", key) + if item.IsUndefined() { + err = newItemNotFoundError( + "Could not find an item with the given key: %s", key) + } else { + s = item.String() + } + return s, err +} + +// Key finds and returns the key associated with the given item. If the item is +// not in localStorage, Key will return an ItemNotFoundError. +func Key(item string) (s string, err error) { + if localStorage.IsUndefined() { + return "", ErrLocalStorageNotSupported + } + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("could not use local "+ + "storage: %v", r) + } + s = "" + } + }() + + key := localStorage.Call("key", item) + if key.IsUndefined() { + err = newItemNotFoundError( + "Could not find a key for the given item: %s", item) + } else { + s = key.String() + } + return s, err +} + +// RemoveItem removes the item with the given key from localStorage. +func RemoveItem(key string) (err error) { + if localStorage.IsUndefined() { + return ErrLocalStorageNotSupported + } + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("could not use local "+ + "storage: %v", r) + } + } + }() + + localStorage.Call("removeItem", key) + return nil +} + +// Length returns the number of items currently in localStorage. +func Length() (l int, err error) { + if localStorage.IsUndefined() { + return 0, ErrLocalStorageNotSupported + } + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("could not use local "+ + "storage: %v", r) + } + l = 0 + } + }() + + length := localStorage.Get("length") + return length.Int(), nil +} + +func ForEach(prefix string, f func(key, value string) error) error { + length, err := Length() + if err != nil { + return err + } + for i := 0; i < length; i++ { + strKey, err := Key(fmt.Sprintf("%d", i)) + if err != nil { + return err + } + + if !strings.HasPrefix(strKey, prefix) { + continue + } + + value, err := GetItem(strKey) + if err != nil { + return err + } + + err = f(strKey, value) + if err != nil { + return err + } + } + + return nil +} diff --git a/clientdb/event.go b/clientdb/event.go index 7cbc44b..543f935 100644 --- a/clientdb/event.go +++ b/clientdb/event.go @@ -7,8 +7,8 @@ import ( "sort" "time" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/event" - "go.etcd.io/bbolt" ) var ( @@ -64,7 +64,7 @@ func (db *DB) AllEvents(evtType event.Type) ([]event.Event, error) { // events that match the given predicate. func (db *DB) getEvents(predicate event.Predicate) ([]event.Event, error) { var events []event.Event - err := db.View(func(tx *bbolt.Tx) error { + err := walletdb.View(db, func(tx walletdb.ReadTx) error { var err error events, err = getEventsTX(tx, predicate) return err @@ -78,11 +78,11 @@ func (db *DB) getEvents(predicate event.Predicate) ([]event.Event, error) { // getEventsTX retrieves all events that match the given predicate from the main // event bucket within the given database transaction. -func getEventsTX(tx *bbolt.Tx, predicate event.Predicate) ([]event.Event, +func getEventsTX(tx walletdb.ReadTx, predicate event.Predicate) ([]event.Event, error) { events := make([]event.Event, 0, eventsAllocSize) - eventBucket := tx.Bucket(eventBucketKey) + eventBucket := tx.ReadBucket(eventBucketKey) err := eventBucket.ForEach(func(k, v []byte) error { // There shouldn't be any other keys below the main // event bucket so we fail hard if a key doesn't match @@ -133,7 +133,7 @@ func getEventsTX(tx *bbolt.Tx, predicate event.Predicate) ([]event.Event, // storeEventTX stores a single event both in the main events bucket (ensuring // the uniqueness of its timestamp in the process) and a reference to it in the // "owner"'s bucket under an event reference sub bucket. -func storeEventTX(ownerBucket *bbolt.Bucket, evt event.Event) error { +func storeEventTX(ownerBucket walletdb.ReadWriteBucket, evt event.Event) error { // For convenience, the event is allowed to be nil, in case no update // really happened. if evt == nil { @@ -141,7 +141,7 @@ func storeEventTX(ownerBucket *bbolt.Bucket, evt event.Event) error { } // First of all, write the event to the global event bucket. - evtBucket := ownerBucket.Tx().Bucket(eventBucketKey) + evtBucket := ownerBucket.Tx().ReadWriteBucket(eventBucketKey) err := event.StoreEvent(evtBucket, evt) if err != nil { return err diff --git a/clientdb/event_test.go b/clientdb/event_test.go index e551f7a..a99db30 100644 --- a/clientdb/event_test.go +++ b/clientdb/event_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/event" "github.com/lightninglabs/pool/order" "github.com/stretchr/testify/require" - "go.etcd.io/bbolt" ) // TestOrderEvents tests that storing and updating orders creates the correct @@ -117,10 +117,10 @@ func assertOrderCreateEvent(t *testing.T, store *DB, o order.Nonce) { func getOrderEventTimestamps(store *DB, o order.Nonce, evtType event.Type) ( map[time.Time]struct{}, error) { orderEventTimestamps := make(map[time.Time]struct{}) - err := store.DB.View(func(tx *bbolt.Tx) error { - ordersBucket := tx.Bucket(ordersBucketKey) - orderBucket := ordersBucket.Bucket(o[:]) - eventBucket := orderBucket.Bucket(eventRefSubBucket) + err := walletdb.View(store, func(tx walletdb.ReadTx) error { + ordersBucket := tx.ReadBucket(ordersBucketKey) + orderBucket := ordersBucket.NestedReadBucket(o[:]) + eventBucket := orderBucket.NestedReadBucket(eventRefSubBucket) return eventBucket.ForEach(func(k, v []byte) error { // Only look at keys with correct length. if len(k) != event.TimestampLength { diff --git a/clientdb/metadata.go b/clientdb/metadata.go index 82e5663..4d62444 100644 --- a/clientdb/metadata.go +++ b/clientdb/metadata.go @@ -5,15 +5,15 @@ import ( "errors" "fmt" + "github.com/btcsuite/btcwallet/walletdb" "github.com/btcsuite/btcwallet/wtxmgr" "github.com/lightninglabs/pool/clientdb/migrations" - "go.etcd.io/bbolt" ) // migration is a function which takes a prior outdated version of the database // instance and mutates the key/bucket structure to arrive at a more up-to-date // version of the database. -type migration func(tx *bbolt.Tx) error +type migration func(tx walletdb.ReadWriteTx) error var ( // metadataBucketKey stores all the metadata concerning the state of the @@ -34,6 +34,8 @@ var ( // ErrDBReversion is returned when detecting an attempt to revert to a // prior database version. ErrDBReversion = errors.New("cannot revert to prior version") + + ErrDBVersionNotFound = errors.New("database version not found") // dbVersions is storing all versions of database. If current version // of database don't match with latest version this list will be used @@ -47,24 +49,38 @@ var ( ) // getDBVersion retrieves the current database version. -func getDBVersion(bucket *bbolt.Bucket) (uint32, error) { +func getDBVersion(bucket walletdb.ReadBucket) (uint32, error) { versionBytes := bucket.Get(dbVersionKey) if versionBytes == nil { - return 0, errors.New("database version not found") + return 0, ErrDBVersionNotFound } return byteOrder.Uint32(versionBytes), nil } // setDBVersion updates the current database version. -func setDBVersion(bucket *bbolt.Bucket, version uint32) error { +func setDBVersion(bucket walletdb.ReadWriteBucket, version uint32) error { var b [4]byte byteOrder.PutUint32(b[:], version) return bucket.Put(dbVersionKey, b[:]) } -// getBucket retrieves the bucket with the given key. -func getBucket(tx *bbolt.Tx, key []byte) (*bbolt.Bucket, error) { - bucket := tx.Bucket(key) +// getWriteBucket retrieves the bucket with the given key in write mode. +func getWriteBucket(tx walletdb.ReadWriteTx, + key []byte) (walletdb.ReadWriteBucket, error) { + + bucket := tx.ReadWriteBucket(key) + if bucket == nil { + return nil, fmt.Errorf("bucket \"%v\" does not exist", + string(key)) + } + return bucket, nil +} + +// getReadBucket retrieves the bucket with the given key in read mode. +func getReadBucket(tx walletdb.ReadTx, + key []byte) (walletdb.ReadBucket, error) { + + bucket := tx.ReadBucket(key) if bucket == nil { return nil, fmt.Errorf("bucket \"%v\" does not exist", string(key)) @@ -75,10 +91,10 @@ func getBucket(tx *bbolt.Tx, key []byte) (*bbolt.Bucket, error) { // getNestedBucket retrieves the nested bucket with the given key found within // the given bucket. If the bucket does not exist and `create` is true, then the // bucket is created. -func getNestedBucket(bucket *bbolt.Bucket, key []byte, - create bool) (*bbolt.Bucket, error) { +func getNestedBucket(bucket walletdb.ReadWriteBucket, key []byte, + create bool) (walletdb.ReadWriteBucket, error) { - nestedBucket := bucket.Bucket(key) + nestedBucket := bucket.NestedReadWriteBucket(key) if nestedBucket == nil && create { return bucket.CreateBucketIfNotExists(key) } @@ -89,13 +105,24 @@ func getNestedBucket(bucket *bbolt.Bucket, key []byte, return nestedBucket, nil } +func getNestedReadBucket(bucket walletdb.ReadBucket, + key []byte) (walletdb.ReadBucket, error) { + + nestedBucket := bucket.NestedReadBucket(key) + if nestedBucket == nil { + return nil, fmt.Errorf("nested bucket \"%v\" does not exist", + string(key)) + } + return nestedBucket, nil +} + // syncVersions function is used for safe db version synchronization. It // applies migration functions to the current database and recovers the // previous state of db if at least one error/panic appeared during migration. -func syncVersions(db *bbolt.DB) error { +func syncVersions(db walletdb.DB) error { var currentVersion uint32 - err := db.View(func(tx *bbolt.Tx) error { - metadata, err := getBucket(tx, metadataBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + metadata, err := getReadBucket(tx, metadataBucketKey) if err != nil { return err } @@ -131,7 +158,7 @@ func syncVersions(db *bbolt.DB) error { // Otherwise we execute the migrations serially within a single database // transaction to ensure the migration is atomic. - return db.Update(func(tx *bbolt.Tx) error { + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { for v := currentVersion; v < latestDBVersion; v++ { log.Infof("Applying migration #%v", v+1) @@ -142,7 +169,7 @@ func syncVersions(db *bbolt.DB) error { } } - metadata, err := getBucket(tx, metadataBucketKey) + metadata, err := getWriteBucket(tx, metadataBucketKey) if err != nil { return err } @@ -152,7 +179,7 @@ func syncVersions(db *bbolt.DB) error { // storeRandomLockID generates a random lock ID backed by the system's CSPRNG // and stores it under the metadata bucket. -func storeRandomLockID(metadata *bbolt.Bucket) error { +func storeRandomLockID(metadata walletdb.ReadWriteBucket) error { var lockID wtxmgr.LockID if _, err := rand.Read(lockID[:]); err != nil { return err @@ -164,8 +191,8 @@ func storeRandomLockID(metadata *bbolt.Bucket) error { // backing lnd node's wallet. func (db *DB) LockID() (wtxmgr.LockID, error) { var lockID wtxmgr.LockID - err := db.View(func(tx *bbolt.Tx) error { - metadata, err := getBucket(tx, metadataBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + metadata, err := getReadBucket(tx, metadataBucketKey) if err != nil { return err } diff --git a/clientdb/migrations/migration_01_add_events.go b/clientdb/migrations/migration_01_add_events.go index 844e236..155c3bd 100644 --- a/clientdb/migrations/migration_01_add_events.go +++ b/clientdb/migrations/migration_01_add_events.go @@ -6,8 +6,8 @@ import ( "fmt" "time" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/event" - "go.etcd.io/bbolt" ) var ( @@ -35,7 +35,7 @@ var ( // AddInitialOrderTimestamps creates an "order created" timestamp for each of // the existing orders. This will only be executed once, when the main event // bucket is created for the first time. -func AddInitialOrderTimestamps(tx *bbolt.Tx) error { +func AddInitialOrderTimestamps(tx walletdb.ReadWriteTx) error { // We back date all existing orders to the beginning of the year of hell // to signal this isn't the real timestamp it was created at but // something we added later. @@ -44,7 +44,7 @@ func AddInitialOrderTimestamps(tx *bbolt.Tx) error { ).UnixNano() // First, we'll grab our main order bucket key. - ordersBucket := tx.Bucket(ordersBucketKey) + ordersBucket := tx.ReadWriteBucket(ordersBucketKey) if ordersBucket == nil { return fmt.Errorf("bucket \"%v\" does not exist", string(ordersBucketKey)) @@ -69,7 +69,7 @@ func AddInitialOrderTimestamps(tx *bbolt.Tx) error { // creation event for that order. var nonce [32]byte copy(nonce[:], nonceBytes) - orderBucket := ordersBucket.Bucket(nonce[:]) + orderBucket := ordersBucket.NestedReadWriteBucket(nonce[:]) if orderBucket == nil { return fmt.Errorf("order bucket not found") } @@ -83,7 +83,7 @@ func AddInitialOrderTimestamps(tx *bbolt.Tx) error { // With the key encoded, we'll then encode the event into our // buffer, then write it out to disk. - evtBucket := tx.Bucket(eventBucketKey) + evtBucket := tx.ReadWriteBucket(eventBucketKey) var eventBuf bytes.Buffer if err := eventBuf.WriteByte(eventType); err != nil { return err @@ -107,7 +107,7 @@ func AddInitialOrderTimestamps(tx *bbolt.Tx) error { // Because we can't modify the order bucket in the ForEach above // directly, we need to insert the references outside of the callback. for nonce, ts := range eventRefs { - orderBucket := ordersBucket.Bucket(nonce[:]) + orderBucket := ordersBucket.NestedReadWriteBucket(nonce[:]) if orderBucket == nil { return fmt.Errorf("order bucket not found") } diff --git a/clientdb/order.go b/clientdb/order.go index 115b503..32a3f34 100644 --- a/clientdb/order.go +++ b/clientdb/order.go @@ -7,11 +7,11 @@ import ( "io" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/event" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/sidecar" "github.com/lightningnetwork/lnd/tlv" - "go.etcd.io/bbolt" ) const ( @@ -80,8 +80,8 @@ type orderCallback func(nonce order.Nonce, rawOrder []byte, // // NOTE: This is part of the Store interface. func (db *DB) SubmitOrder(newOrder order.Order) error { - return db.Update(func(tx *bbolt.Tx) error { - rootBucket, err := getBucket(tx, ordersBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + rootBucket, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -152,8 +152,8 @@ func (db *DB) SubmitOrder(newOrder order.Order) error { // // NOTE: This is part of the Store interface. func (db *DB) UpdateOrder(nonce order.Nonce, modifiers ...order.Modifier) error { - return db.Update(func(tx *bbolt.Tx) error { - rootBucket, err := getBucket(tx, ordersBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + rootBucket, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -175,8 +175,8 @@ func (db *DB) UpdateOrders(nonces []order.Nonce, // Read and update the orders in one single transaction that they are // updated atomically. - return db.Update(func(tx *bbolt.Tx) error { - rootBucket, err := getBucket(tx, ordersBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + rootBucket, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -225,8 +225,8 @@ func (db *DB) GetOrder(nonce order.Nonce) (order.Order, error) { return nil } ) - err = db.View(func(tx *bbolt.Tx) error { - rootBucket, err := getBucket(tx, ordersBucketKey) + err = walletdb.View(db, func(tx walletdb.ReadTx) error { + rootBucket, err := getReadBucket(tx, ordersBucketKey) if err != nil { return err } @@ -265,9 +265,9 @@ func (db *DB) GetOrders() ([]order.Order, error) { return nil } ) - err := db.View(func(tx *bbolt.Tx) error { + err := walletdb.View(db, func(tx walletdb.ReadTx) error { // First, we'll grab our main order bucket key. - rootBucket, err := getBucket(tx, ordersBucketKey) + rootBucket, err := getReadBucket(tx, ordersBucketKey) if err != nil { return err } @@ -295,7 +295,7 @@ func (db *DB) GetOrders() ([]order.Order, error) { // storeOrderTX saves a byte serialized order in its specific sub bucket within // the root orders bucket. -func storeOrderTX(rootBucket *bbolt.Bucket, nonce order.Nonce, +func storeOrderTX(rootBucket walletdb.ReadWriteBucket, nonce order.Nonce, orderBytes []byte, evt event.Event) error { // From the root bucket, we'll make a new sub order bucket using @@ -317,7 +317,7 @@ func storeOrderTX(rootBucket *bbolt.Bucket, nonce order.Nonce, // storeOrderMinNoderTierTX saves the current node tier for a given order to // the proper bucket. -func storeOrderMinNoderTierTX(rootBucket *bbolt.Bucket, nonce order.Nonce, +func storeOrderMinNoderTierTX(rootBucket walletdb.ReadWriteBucket, nonce order.Nonce, minNodeTier order.NodeTier) error { orderBucket, err := rootBucket.CreateBucketIfNotExists(nonce[:]) @@ -335,7 +335,7 @@ func storeOrderMinNoderTierTX(rootBucket *bbolt.Bucket, nonce order.Nonce, // storeOrderMinUnitsMatchTX saves the order's minimum units match within the // order's root bucket. -func storeOrderMinUnitsMatchTX(rootBucket *bbolt.Bucket, nonce order.Nonce, +func storeOrderMinUnitsMatchTX(rootBucket walletdb.ReadWriteBucket, nonce order.Nonce, minUnitsMatch order.SupplyUnit) error { // From the root bucket, we'll make a new sub order bucket using @@ -356,7 +356,7 @@ func storeOrderMinUnitsMatchTX(rootBucket *bbolt.Bucket, nonce order.Nonce, // storeOrderTlvTX saves the order's additional information as a tlv stream // within the order's root bucket. -func storeOrderTlvTX(rootBucket *bbolt.Bucket, nonce order.Nonce, +func storeOrderTlvTX(rootBucket walletdb.ReadWriteBucket, nonce order.Nonce, o order.Order) error { orderBucket, err := rootBucket.CreateBucketIfNotExists(nonce[:]) @@ -374,11 +374,11 @@ func storeOrderTlvTX(rootBucket *bbolt.Bucket, nonce order.Nonce, // fetchOrderTX fetches the binary data of one order specified by its nonce from // the root orders bucket. -func fetchOrderTX(rootBucket *bbolt.Bucket, nonce order.Nonce, +func fetchOrderTX(rootBucket walletdb.ReadBucket, nonce order.Nonce, callback orderCallback) error { // Get the main order bucket next. - orderBucket := rootBucket.Bucket(nonce[:]) + orderBucket := rootBucket.NestedReadBucket(nonce[:]) if orderBucket == nil { return ErrNoOrder } @@ -431,7 +431,7 @@ func fetchOrderTX(rootBucket *bbolt.Bucket, nonce order.Nonce, // update should be written to a staging area instead of being applied to the // original order directly. Do not use this function for applying the staged // changes to the final bucket but use copyOrder instead. -func updateOrder(ordersBucket, dst *bbolt.Bucket, nonce order.Nonce, +func updateOrder(ordersBucket, dst walletdb.ReadWriteBucket, nonce order.Nonce, modifiers []order.Modifier) (order.Order, error) { var ( @@ -484,7 +484,7 @@ func updateOrder(ordersBucket, dst *bbolt.Bucket, nonce order.Nonce, // reference to the order in the main order bucket, even if the // destination bucket is the pending orders bucket. evt := NewUpdatedEvent(prevState, o) - orderBucket := ordersBucket.Bucket(nonce[:]) + orderBucket := ordersBucket.NestedReadWriteBucket(nonce[:]) if orderBucket == nil { return nil, ErrNoOrder } @@ -522,7 +522,7 @@ func updateOrder(ordersBucket, dst *bbolt.Bucket, nonce order.Nonce, // copyOrder copies a single order from the source to destination bucket. No // event references are copied, only the order itself. -func copyOrder(src, dst *bbolt.Bucket, nonce order.Nonce) error { +func copyOrder(src, dst walletdb.ReadWriteBucket, nonce order.Nonce) error { var ( o order.Order orderBytes []byte diff --git a/clientdb/order_event.go b/clientdb/order_event.go index 7645506..6f06652 100644 --- a/clientdb/order_event.go +++ b/clientdb/order_event.go @@ -5,11 +5,11 @@ import ( "io" "time" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/auctioneerrpc" "github.com/lightninglabs/pool/event" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/poolrpc" - "go.etcd.io/bbolt" ) // OrderEvent is the main interface for order specific events. @@ -317,18 +317,18 @@ var _ OrderEvent = (*MatchEvent)(nil) // reference keys in the order bucket. func (db *DB) GetOrderEvents(o order.Nonce) ([]event.Event, error) { var events []event.Event - err := db.View(func(tx *bbolt.Tx) error { - ordersBucket, err := getBucket(tx, ordersBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + ordersBucket, err := getReadBucket(tx, ordersBucketKey) if err != nil { return err } - orderBucket := ordersBucket.Bucket(o[:]) + orderBucket := ordersBucket.NestedReadBucket(o[:]) if orderBucket == nil { return ErrNoOrder } - eventSubBucket := orderBucket.Bucket(eventRefSubBucket) + eventSubBucket := orderBucket.NestedReadBucket(eventRefSubBucket) if eventSubBucket == nil { return fmt.Errorf("order event sub bucket not found") } @@ -388,8 +388,8 @@ func (db *DB) StoreOrderEvents(events []OrderEvent) error { } event.MakeUniqueTimestamps(baseEvents) - return db.Update(func(tx *bbolt.Tx) error { - ordersBucket, err := getBucket(tx, ordersBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + ordersBucket, err := getWriteBucket(tx, ordersBucketKey) if err != nil { return err } @@ -398,7 +398,7 @@ func (db *DB) StoreOrderEvents(events []OrderEvent) error { // up the individual order bucket in each iteration. for _, evt := range events { nonce := evt.Nonce() - orderBucket := ordersBucket.Bucket(nonce[:]) + orderBucket := ordersBucket.NestedReadWriteBucket(nonce[:]) if orderBucket == nil { return ErrNoOrder } diff --git a/clientdb/sidecar.go b/clientdb/sidecar.go index ac30fad..627b866 100644 --- a/clientdb/sidecar.go +++ b/clientdb/sidecar.go @@ -6,9 +6,9 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/sidecar" - "go.etcd.io/bbolt" ) var ( @@ -60,8 +60,8 @@ func (db *DB) AddSidecar(ticket *sidecar.Ticket) error { return err } - return db.Update(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + sidecarBucket, err := getWriteBucket(tx, sidecarsBucketKey) if err != nil { return err } @@ -92,8 +92,8 @@ func (db *DB) AddSidecarWithBid(ticket *sidecar.Ticket, bid *order.Bid) error { bidNonce := bid.Nonce() copy(ticket.Order.BidNonce[:], bidNonce[:]) - return db.Update(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + sidecarBucket, err := getWriteBucket(tx, sidecarsBucketKey) if err != nil { return err } @@ -126,8 +126,8 @@ func (db *DB) UpdateSidecar(ticket *sidecar.Ticket) error { return err } - return db.Update(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + sidecarBucket, err := getWriteBucket(tx, sidecarsBucketKey) if err != nil { return err } @@ -154,8 +154,8 @@ func (db *DB) Sidecar(id [8]byte, } var s *sidecar.Ticket - err = db.View(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + err = walletdb.View(db, func(tx walletdb.ReadTx) error { + sidecarBucket, err := getReadBucket(tx, sidecarsBucketKey) if err != nil { return err } @@ -175,13 +175,13 @@ func (db *DB) Sidecar(id [8]byte, func (db *DB) SidecarBidTemplate(ticket *sidecar.Ticket) (*order.Bid, error) { var bid *order.Bid - err := db.View(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + sidecarBucket, err := getReadBucket(tx, sidecarsBucketKey) if err != nil { return err } - bidBucket := sidecarBucket.Bucket(bidTemplateBucket) + bidBucket := sidecarBucket.NestedReadBucket(bidTemplateBucket) if bidBucket == nil { return err } @@ -199,8 +199,8 @@ func (db *DB) SidecarBidTemplate(ticket *sidecar.Ticket) (*order.Bid, error) { // Sidecars retrieves all known sidecars from the database. func (db *DB) Sidecars() ([]*sidecar.Ticket, error) { var res []*sidecar.Ticket - err := db.View(func(tx *bbolt.Tx) error { - sidecarBucket, err := getBucket(tx, sidecarsBucketKey) + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + sidecarBucket, err := getReadBucket(tx, sidecarsBucketKey) if err != nil { return err } @@ -227,7 +227,7 @@ func (db *DB) Sidecars() ([]*sidecar.Ticket, error) { return res, nil } -func storeSidecar(targetBucket *bbolt.Bucket, key []byte, +func storeSidecar(targetBucket walletdb.ReadWriteBucket, key []byte, ticket *sidecar.Ticket) error { var sidecarBuf bytes.Buffer @@ -240,7 +240,7 @@ func storeSidecar(targetBucket *bbolt.Bucket, key []byte, return targetBucket.Put(key, sidecarBuf.Bytes()) } -func readSidecar(sourceBucket *bbolt.Bucket, id []byte) (*sidecar.Ticket, +func readSidecar(sourceBucket walletdb.ReadBucket, id []byte) (*sidecar.Ticket, error) { sidecarBytes := sourceBucket.Get(id) @@ -251,7 +251,9 @@ func readSidecar(sourceBucket *bbolt.Bucket, id []byte) (*sidecar.Ticket, return sidecar.DeserializeTicket(bytes.NewReader(sidecarBytes)) } -func storeBidTemplate(bidBucket *bbolt.Bucket, bid *order.Bid, ticketNonce order.Nonce) error { +func storeBidTemplate(bidBucket walletdb.ReadWriteBucket, bid *order.Bid, + ticketNonce order.Nonce) error { + var w bytes.Buffer if err := SerializeOrder(bid, &w); err != nil { return err @@ -273,7 +275,7 @@ func storeBidTemplate(bidBucket *bbolt.Bucket, bid *order.Bid, ticketNonce order return storeOrderMinNoderTierTX(bidBucket, ticketNonce, bid.MinNodeTier) } -func readBidTemplate(bidBucket *bbolt.Bucket, +func readBidTemplate(bidBucket walletdb.ReadBucket, ticketNonce order.Nonce) (*order.Bid, error) { var ( diff --git a/cmd/pool/debug.go b/cmd/pool/debug.go index aad81ac..85175da 100644 --- a/cmd/pool/debug.go +++ b/cmd/pool/debug.go @@ -12,7 +12,6 @@ import ( "github.com/lightninglabs/pool/order" "github.com/lightninglabs/pool/poolrpc" "github.com/lightninglabs/pool/terms" - "github.com/lightningnetwork/lnd/lncfg" "github.com/urfave/cli" ) @@ -233,7 +232,7 @@ func getPoolDB(ctx *cli.Context) (*clientdb.DB, error) { clientdb.DBFilename, ) if ctx.IsSet("db") { - fullDbPath = lncfg.CleanAndExpandPath(ctx.String("db")) + fullDbPath = pool.CleanAndExpandPath(ctx.String("db")) } db, err := clientdb.New(path.Dir(fullDbPath), path.Base(fullDbPath)) diff --git a/cmd/pool/main.go b/cmd/pool/main.go index 902fbec..cbef247 100644 --- a/cmd/pool/main.go +++ b/cmd/pool/main.go @@ -19,7 +19,6 @@ import ( "github.com/lightninglabs/protobuf-hex-display/json" "github.com/lightninglabs/protobuf-hex-display/jsonpb" "github.com/lightninglabs/protobuf-hex-display/proto" - "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/macaroons" "github.com/urfave/cli" "google.golang.org/grpc" @@ -208,11 +207,11 @@ func extractPathArgs(ctx *cli.Context) (string, string, error) { // We'll now fetch the basedir so we can make a decision on how to // properly read the cert and macaroon. This will either be the default, // or will have been overwritten by the end user. - baseDir := lncfg.CleanAndExpandPath(ctx.GlobalString(baseDirFlag.Name)) - tlsCertPath := lncfg.CleanAndExpandPath(ctx.GlobalString( + baseDir := pool.CleanAndExpandPath(ctx.GlobalString(baseDirFlag.Name)) + tlsCertPath := pool.CleanAndExpandPath(ctx.GlobalString( tlsCertFlag.Name, )) - macPath := lncfg.CleanAndExpandPath(ctx.GlobalString( + macPath := pool.CleanAndExpandPath(ctx.GlobalString( macaroonPathFlag.Name, )) diff --git a/config.go b/config.go index d736ff3..bf806a2 100644 --- a/config.go +++ b/config.go @@ -6,13 +6,15 @@ import ( "fmt" "net" "os" + "os/user" "path" "path/filepath" + "runtime" + "strings" "time" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/cert" - "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -42,6 +44,11 @@ var ( defaultMinBackoff = 5 * time.Second defaultMaxBackoff = 1 * time.Minute + // DefaultAutogenValidity is the default validity of a self-signed + // certificate. The value corresponds to 14 months + // (14 months * 30 days * 24 hours). + DefaultAutogenValidity = 14 * 30 * 24 * time.Hour + // DefaultTLSCertFilename is the default file name for the autogenerated // TLS certificate. DefaultTLSCertFilename = "tls.cert" @@ -105,6 +112,9 @@ type LndConfig struct { MacaroonPath string `long:"macaroonpath" description:"The full path to the single macaroon to use, either the admin.macaroon or a custom baked one. Cannot be specified at the same time as macaroondir. A custom macaroon must contain ALL permissions required for all subservers to work, otherwise permission errors will occur."` TLSPath string `long:"tlspath" description:"Path to lnd tls certificate"` + + ConnectMailbox string `long:"connectmailbox" description:"Use the mailbox with the given address to connect to lnd instead"` + MailboxPassword string `long:"mailboxpassword" description:"The password for connecting to the mailbox"` } type Config struct { @@ -193,11 +203,11 @@ func DefaultConfig() Config { // Validate cleans up paths in the config provided and validates it. func Validate(cfg *Config) error { // Cleanup any paths before we use them. - cfg.BaseDir = lncfg.CleanAndExpandPath(cfg.BaseDir) - cfg.LogDir = lncfg.CleanAndExpandPath(cfg.LogDir) - cfg.TLSCertPath = lncfg.CleanAndExpandPath(cfg.TLSCertPath) - cfg.TLSKeyPath = lncfg.CleanAndExpandPath(cfg.TLSKeyPath) - cfg.MacaroonPath = lncfg.CleanAndExpandPath(cfg.MacaroonPath) + cfg.BaseDir = CleanAndExpandPath(cfg.BaseDir) + cfg.LogDir = CleanAndExpandPath(cfg.LogDir) + cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath) + cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath) + cfg.MacaroonPath = CleanAndExpandPath(cfg.MacaroonPath) // Since our pool directory overrides our log and TLS dir values, make // sure that they are not set when base dir is set. We hard here rather @@ -260,12 +270,14 @@ func Validate(cfg *Config) error { } // If either of these directories do not exist, create them. - if err := os.MkdirAll(cfg.BaseDir, os.ModePerm); err != nil { - return err - } + if runtime.GOOS != "js" { + if err := os.MkdirAll(cfg.BaseDir, os.ModePerm); err != nil { + return err + } - if err := os.MkdirAll(cfg.LogDir, os.ModePerm); err != nil { - return err + if err := os.MkdirAll(cfg.LogDir, os.ModePerm); err != nil { + return err + } } // Make sure only one of the macaroon options is used. @@ -281,12 +293,12 @@ func Validate(cfg *Config) error { // macaroondir is used, we use the admin macaroon located in // that directory. cfg.Lnd.MacaroonPath = path.Join( - lncfg.CleanAndExpandPath(cfg.Lnd.MacaroonDir), + CleanAndExpandPath(cfg.Lnd.MacaroonDir), defaultLndMacaroon, ) case cfg.Lnd.MacaroonPath != "": - cfg.Lnd.MacaroonPath = lncfg.CleanAndExpandPath( + cfg.Lnd.MacaroonPath = CleanAndExpandPath( cfg.Lnd.MacaroonPath, ) @@ -370,7 +382,7 @@ func loadCertWithCreate(cfg *Config) (tls.Certificate, *x509.Certificate, defaultSelfSignedOrganization, cfg.TLSCertPath, cfg.TLSKeyPath, cfg.TLSExtraIPs, cfg.TLSExtraDomains, cfg.TLSDisableAutofill, - cert.DefaultAutogenValidity, + DefaultAutogenValidity, ) if err != nil { return tls.Certificate{}, nil, err @@ -380,3 +392,29 @@ func loadCertWithCreate(cfg *Config) (tls.Certificate, *x509.Certificate, return cert.LoadCert(cfg.TLSCertPath, cfg.TLSKeyPath) } + +// CleanAndExpandPath expands environment variables and leading ~ in the +// passed path, cleans the result, and returns it. +// This function is taken from https://github.com/btcsuite/btcd +func CleanAndExpandPath(path string) string { + if path == "" { + return "" + } + + // Expand initial ~ to OS specific home directory. + if strings.HasPrefix(path, "~") { + var homeDir string + u, err := user.Current() + if err == nil { + homeDir = u.HomeDir + } else { + homeDir = os.Getenv("HOME") + } + + path = strings.Replace(path, "~", homeDir, 1) + } + + // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, + // but the variables can still be expanded via POSIX-style $VARIABLE. + return filepath.Clean(os.ExpandEnv(path)) +} diff --git a/event/event.go b/event/event.go index 1eead45..b9bf753 100644 --- a/event/event.go +++ b/event/event.go @@ -8,7 +8,7 @@ import ( "sort" "time" - "go.etcd.io/bbolt" + "github.com/btcsuite/btcwallet/walletdb" ) const ( @@ -96,7 +96,7 @@ type Predicate func(time.Time, Type) bool // function should be called first to ensure the timestamps are already unique // among themselves. If no "gap" in the timestamps can be found after 1000 tries // an error is returned. -func StoreEvent(bucket *bbolt.Bucket, event Event) error { +func StoreEvent(bucket walletdb.ReadWriteBucket, event Event) error { // We use a fixed length array as our buffer so we don't allocate new // memory for each try. var tsScratchSpace [TimestampLength]byte diff --git a/go.mod b/go.mod index 9da5acc..e3db8c2 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,11 @@ go 1.14 require ( github.com/btcsuite/btcd v0.21.0-beta.0.20210429225535-ce697fe7e82b github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce - github.com/btcsuite/btcwallet v0.12.0 + github.com/btcsuite/btcutil v1.0.3-0.20210514234026-faeebcb9abbe + github.com/btcsuite/btcwallet v0.12.1-0.20210518235050-c31e1497759c github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 + github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210518235050-c31e1497759c // indirect + github.com/btcsuite/btcwallet/walletdb v1.3.5 github.com/btcsuite/btcwallet/wtxmgr v1.3.0 github.com/davecgh/go-spew v1.1.1 github.com/golang/protobuf v1.4.3 @@ -33,3 +35,12 @@ replace github.com/lightninglabs/pool/auctioneerrpc => ./auctioneerrpc // Fix incompatibility of etcd go.mod package. // See https://github.com/etcd-io/etcd/issues/11154 replace go.etcd.io/etcd => go.etcd.io/etcd v0.5.0-alpha.5.0.20201125193152-8a03d2e9614b + +replace github.com/lightningnetwork/lnd => ../../lightningnetwork/lnd + +replace github.com/lightningnetwork/lnd/healthcheck => ../../lightningnetwork/lnd/healthcheck + +replace github.com/lightningnetwork/lnd/kvdb => ../../lightningnetwork/lnd/kvdb +replace github.com/lightningnetwork/lnd/cert => ../../lightningnetwork/lnd/cert + +replace github.com/lightninglabs/lndclient => ../lndclient diff --git a/go.sum b/go.sum index 02911de..a2cf90b 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,6 @@ github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQ github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2 h1:2be4ykKKov3M1yISM2E8gnGXZ/N2SsPawfnGiXxaYEU= github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= @@ -27,44 +26,44 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.20.1-beta.0.20200730232343-1db1b6f8217f/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd v0.21.0-beta.0.20201208033208-6bd4c64a54fa/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= github.com/btcsuite/btcd v0.21.0-beta.0.20210426180113-7eba688b65e5/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= +github.com/btcsuite/btcd v0.21.0-beta.0.20210426180113-7eba688b65e5/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= github.com/btcsuite/btcd v0.21.0-beta.0.20210429225535-ce697fe7e82b h1:+BMyRYlNCozjv6zGhnyHExB6JquAIYZRwsc5bxPEXrI= github.com/btcsuite/btcd v0.21.0-beta.0.20210429225535-ce697fe7e82b/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= -github.com/btcsuite/btcutil/psbt v1.0.3-0.20200826194809-5f93e33af2b0/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= -github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce h1:3PRwz+js0AMMV1fHRrCdQ55akoomx4Q3ulozHC3BDDY= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/btcsuite/btcutil v1.0.3-0.20210514234026-faeebcb9abbe h1:SdSUcRKxJEWfRCfZryp2YKn632Lh6UoTBfLi+cv1GNo= +github.com/btcsuite/btcutil v1.0.3-0.20210514234026-faeebcb9abbe/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= -github.com/btcsuite/btcwallet v0.11.1-0.20200814001439-1d31f4ea6fc5/go.mod h1:YkEbJaCyN6yncq5gEp2xG0OKDwus2QxGCEXTNF27w5I= -github.com/btcsuite/btcwallet v0.11.1-0.20200904022754-2c5947a45222/go.mod h1:owv9oZqM0HnUW+ByF7VqOgfs2eb0ooiePW/+Tl/i/Nk= -github.com/btcsuite/btcwallet v0.11.1-0.20201207233335-415f37ff11a1/go.mod h1:P1U4LKSB/bhFQdOM7ab1XqNoBGFyFAe7eKObEBD9mIo= -github.com/btcsuite/btcwallet v0.12.0 h1:0kT0rDN8vNcAvuHp2qUS/hLsfa0VUn2dNQ2GvM9ozBA= +github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= +github.com/btcsuite/btcutil/psbt v1.0.3-0.20210514234026-faeebcb9abbe h1:+zkzfj2bGHMOuxxf8xG3d5mDvd7phT7IyVv+8sQizqc= +github.com/btcsuite/btcutil/psbt v1.0.3-0.20210514234026-faeebcb9abbe/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= github.com/btcsuite/btcwallet v0.12.0/go.mod h1:f1HuBGov5+OTp40Gh1vA+tvF6d7bbuLFTceJMRB7fXw= +github.com/btcsuite/btcwallet v0.12.1-0.20210518235050-c31e1497759c h1:iCyE00LfXe92XqdTU4zCPvUZhm8VQZUT3WwPrEesNBQ= +github.com/btcsuite/btcwallet v0.12.1-0.20210518235050-c31e1497759c/go.mod h1:f1HuBGov5+OTp40Gh1vA+tvF6d7bbuLFTceJMRB7fXw= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6 h1:mO7NxcfgLe75paLDHx+LWNG5BskiDQigHnSVT2KvNZA= +github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6 h1:mO7NxcfgLe75paLDHx+LWNG5BskiDQigHnSVT2KvNZA= +github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w= github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA= github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= -github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210329233242-e0607006dce6 h1:n9SLPLz2PRg2X+lnWxioxTmtAa2ZqjR8EwL/tZD7BAY= github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= -github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk= -github.com/btcsuite/btcwallet/walletdb v1.2.0/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc= +github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= +github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210518235050-c31e1497759c h1:m7dBYDQC0cHyH3WKJPNph3P5bealrqQzrcGhECypj6s= +github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210518235050-c31e1497759c/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= github.com/btcsuite/btcwallet/walletdb v1.3.2/go.mod h1:GZCMPNpUu5KE3ASoVd+k06p/1OW8OwNGCCaNWRto2cQ= -github.com/btcsuite/btcwallet/walletdb v1.3.3/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/walletdb v1.3.4/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/walletdb v1.3.5-0.20210513043850-3a2f12e3a954/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= github.com/btcsuite/btcwallet/walletdb v1.3.5 h1:SoxUPLgJUkyO1XqON6X7x+rjHJoIpRQov8o8X6gNoz8= github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= -github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY= github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY= github.com/btcsuite/btcwallet/wtxmgr v1.3.0 h1:lrZaZXGJjDedYTV7s5UgU9xBe8+N+cIDW7BYwI/B8Fs= github.com/btcsuite/btcwallet/wtxmgr v1.3.0/go.mod h1:awQsh1n/0ZrEQ+JZgWvHeo153ubzEisf/FyNtwI0dDk= @@ -82,10 +81,11 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.22+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -107,7 +107,7 @@ github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8 github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -125,14 +125,31 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -146,13 +163,22 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -168,10 +194,13 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -182,6 +211,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4G github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc= @@ -220,6 +250,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec h1:n1NeQ3SgUHyISrjFFoO5dR748Is8dBL9qpaTNfphQrs= github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -228,37 +260,25 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lightninglabs/aperture v0.1.6-beta h1:bhpK4O9xa0YUBFQfkfg/h/3sMAY+AOMxi9YUjg6/l/E= github.com/lightninglabs/aperture v0.1.6-beta/go.mod h1:9xl4mx778ZAzrB87nLHMqk+XQcSz8Dx/DypjWzGN1xo= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/lndclient v0.11.0-4/go.mod h1:8/cTKNwgL87NX123gmlv3Xh6p1a7pvzu+40Un3PhHiI= -github.com/lightninglabs/lndclient v0.12.0-8 h1:ZnX8wp0atNsBNGNIs+MYvLDRRnoia63RPflD9OfeonQ= -github.com/lightninglabs/lndclient v0.12.0-8/go.mod h1:L0R2VOaLxMylGbxgnfiZGc0hMDIIgj91cfgwGuFz9kU= -github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= -github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= -github.com/lightninglabs/neutrino v0.11.1-0.20201210023533-e1978372d15e/go.mod h1:KDWfQDKp+CFBxO1t2NRmWuagTY2sYIjpHB1k5vrojTI= github.com/lightninglabs/neutrino v0.12.1 h1:9umzk5kKNc/l3bAyak8ClSRP1qSulnjc6kppLYDnuqk= github.com/lightninglabs/neutrino v0.12.1/go.mod h1:GlKninWpRBbL7b8G0oQ36/8downfnFwKsr0hbRA6E/E= -github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI= github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ9pFtx5wqsuD3B/DezrewJeVwDKKYY8E= +github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ9pFtx5wqsuD3B/DezrewJeVwDKKYY8E= +github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE= github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE= github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k= github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4= -github.com/lightningnetwork/lnd v0.11.0-beta/go.mod h1:CzArvT7NFDLhVyW06+NJWSuWFmE6Ea+AjjA3txUBqTM= -github.com/lightningnetwork/lnd v0.11.1-beta/go.mod h1:PGIgxy8aH70Li33YVYkHSaCM8m8LjEevk5h1Dpldrr4= -github.com/lightningnetwork/lnd v0.12.0-beta/go.mod h1:2GyP1IG1kXV5Af/LOCxnXfux1OP3fAGr8zptS5PB2YI= -github.com/lightningnetwork/lnd v0.13.0-beta.rc2 h1:Pg1Qa0LPwOJXYt/vr3NYZwnn/RiwERxeTkZj39x7A1E= -github.com/lightningnetwork/lnd v0.13.0-beta.rc2/go.mod h1:uPP8n+uTsMC7WDpO6tosvVwI2B51wgHptMzWDXcwq2M= github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo= github.com/lightningnetwork/lnd/cert v1.0.3 h1:/K2gjzLgVI8we2IIPKc0ztWTEa85uds5sWXi1K6mOT0= github.com/lightningnetwork/lnd/cert v1.0.3/go.mod h1:3MWXVLLPI0Mg0XETm9fT4N9Vyy/8qQLmaM5589bEggM= github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo= github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= -github.com/lightningnetwork/lnd/healthcheck v1.0.0 h1:0Cj8aiFB1OANsUuW9P0OlheSPcZEB38rUQXankr8ZpU= -github.com/lightningnetwork/lnd/healthcheck v1.0.0/go.mod h1:u92p1JGFJNMSkMvztKEwmt1P3TRnLeJBXZ3M85xkU1E= -github.com/lightningnetwork/lnd/kvdb v1.0.0 h1:k5nubbfAPYqu4HXySPgKddYVPSVOcuNZtDAiyx8H7rQ= -github.com/lightningnetwork/lnd/kvdb v1.0.0/go.mod h1:NMZdCUmFmS6dezJo1uSUM7GRjKTVksQrIfinoyKIGAI= github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= github.com/lightningnetwork/lnd/queue v1.0.4 h1:8Dq3vxAFSACPy+pKN88oPFhuCpCoAAChPBwa4BJxH4k= github.com/lightningnetwork/lnd/queue v1.0.4/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg= @@ -268,7 +288,12 @@ github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oa github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -283,7 +308,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -297,22 +322,24 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -322,9 +349,11 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= +github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -337,12 +366,16 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E= github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY= -github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= @@ -354,6 +387,7 @@ go.etcd.io/etcd v0.5.0-alpha.5.0.20201125193152-8a03d2e9614b h1:PLwvCoe5rvpuo9Un go.etcd.io/etcd v0.5.0-alpha.5.0.20201125193152-8a03d2e9614b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -407,7 +441,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -422,16 +455,24 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 h1:kHSDPqCtsHZOg0nVylfTo20DDhE9gG4Y0jn7hKQ0QAM= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -504,6 +545,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/index.html b/index.html new file mode 100644 index 0000000..d7f1efe --- /dev/null +++ b/index.html @@ -0,0 +1,62 @@ + + + + + + + Go wasm + + + + + + + + + + + + + diff --git a/lnd.go b/lnd.go new file mode 100644 index 0000000..d2b0fba --- /dev/null +++ b/lnd.go @@ -0,0 +1,61 @@ +// +build !js + +package pool + +import ( + "context" + "path" + + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lnrpc" +) + +// getLnd returns an instance of the lnd services proxy. +func getLnd(network string, cfg *LndConfig) (*lndclient.GrpcLndServices, + lnrpc.LightningClient, error) { + + // We'll want to wait for lnd to be fully synced to its chain backend. + // The call to NewLndServices will block until the sync is completed. + // But we still want to be able to shutdown the daemon if the user + // decides to not wait. For that we can pass down a context that we + // cancel on shutdown. + ctxc, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Make sure the context is canceled if the user requests shutdown. + go func() { + select { + // Client requests shutdown, cancel the wait. + case <-interceptor.ShutdownChannel(): + cancel() + + // The check was completed and the above defer canceled the + // context. We can just exit the goroutine, nothing more to do. + case <-ctxc.Done(): + } + }() + + services, err := lndclient.NewLndServices(&lndclient.LndServicesConfig{ + LndAddress: cfg.Host, + Network: lndclient.Network(network), + CustomMacaroonPath: cfg.MacaroonPath, + TLSPath: cfg.TLSPath, + CheckVersion: minimalCompatibleVersion, + BlockUntilChainSynced: true, + BlockUntilUnlocked: true, + CallerCtx: ctxc, + }) + if err != nil { + return nil, nil, err + } + + basicClient, err := lndclient.NewBasicClient( + cfg.Host, cfg.TLSPath, path.Dir(cfg.MacaroonPath), network, + lndclient.MacFilename(path.Base(cfg.MacaroonPath)), + ) + if err != nil { + return nil, nil, err + } + + return services, basicClient, nil +} diff --git a/lnd_js.go b/lnd_js.go new file mode 100644 index 0000000..42b422d --- /dev/null +++ b/lnd_js.go @@ -0,0 +1,58 @@ +package pool + +import ( + "context" + "crypto/sha512" + "strings" + + "github.com/btcsuite/btcd/btcec" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/mailbox" + "google.golang.org/grpc" +) + +// getLnd returns an instance of the lnd services proxy. +func getLnd(network string, cfg *LndConfig) (*lndclient.GrpcLndServices, + lnrpc.LightningClient, error) { + + conn, err := mailboxClientConnection(cfg) + if err != nil { + return nil, nil, err + } + + services, err := lndclient.NewLndServicesFromAuthConn( + lndclient.Network(network), conn, 0, + ) + return services, lnrpc.NewLightningClient(conn), err +} + +func mailboxClientConnection(cfg *LndConfig) (*grpc.ClientConn, error) { + words := strings.Split(cfg.MailboxPassword, " ") + var mnemonicWords [mailbox.NumPasswordWords]string + copy(mnemonicWords[:], words) + password := mailbox.PasswordMnemonicToEntropy(mnemonicWords) + + sid := sha512.Sum512(password[:]) + receiveSID := mailbox.GetSID(sid, true) + sendSID := mailbox.GetSID(sid, false) + + privKey, err := btcec.NewPrivateKey(btcec.S256()) + if err != nil { + return nil, err + } + ecdh := &keychain.PrivKeyECDH{PrivKey: privKey} + + transportConn := mailbox.NewClientConn(receiveSID, sendSID) + noiseConn := mailbox.NewNoiseConn(ecdh, nil) + + dialOpts := []grpc.DialOption{ + grpc.WithContextDialer(transportConn.Dial), + grpc.WithTransportCredentials(noiseConn), + grpc.WithPerRPCCredentials(noiseConn), + } + + ctx := context.Background() + return grpc.DialContext(ctx, cfg.ConnectMailbox, dialOpts...) +} diff --git a/macaroons.go b/macaroons.go index 8acabbf..96a3dbe 100644 --- a/macaroons.go +++ b/macaroons.go @@ -1,3 +1,5 @@ +// +build !js + package pool import ( diff --git a/macaroons_js.go b/macaroons_js.go new file mode 100644 index 0000000..159fb36 --- /dev/null +++ b/macaroons_js.go @@ -0,0 +1,34 @@ +package pool + +import ( + "context" + + "google.golang.org/grpc" +) + +func (s *Server) startMacaroonService() error { + return nil +} + +func (s *Server) stopMacaroonService() error { + return nil +} + +// macaroonInterceptor creates macaroon security interceptors. +func (s *Server) macaroonInterceptor() (grpc.UnaryServerInterceptor, + grpc.StreamServerInterceptor, error) { + + return noopUnaryInterceptor, noopStreamInterceptor, nil +} + +func noopUnaryInterceptor(ctx context.Context, req interface{}, + _ *grpc.UnaryServerInfo, h grpc.UnaryHandler) (interface{}, error) { + + return h(ctx, req) +} + +func noopStreamInterceptor(srv interface{}, ss grpc.ServerStream, + _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + + return handler(srv, ss) +} diff --git a/rpcserver.go b/rpcserver.go index 22fdee9..0848d65 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -30,7 +30,6 @@ import ( "github.com/lightninglabs/pool/sidecar" "github.com/lightninglabs/pool/terms" "github.com/lightningnetwork/lnd/chanbackup" - lndFunding "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -41,6 +40,8 @@ const ( // getInfoTimeout is the maximum time we allow for the initial getInfo // call to the connected lnd node. getInfoTimeout = 5 * time.Second + + MaxBtcFundingAmount = btcutil.Amount(1<<24) - 1 ) // rpcServer implements the gRPC server on the client side and answers RPC calls @@ -1094,7 +1095,7 @@ func (s *rpcServer) validateOrder(order order.Order, acct *account.Account, // Now that we now how large the order is, ensure that if it's a // wumbo-sized order, then the backing lnd node is advertising wumbo // support. - if order.Details().Amt > lndFunding.MaxBtcFundingAmount && !s.wumboSupported { + if order.Details().Amt > MaxBtcFundingAmount && !s.wumboSupported { return fmt.Errorf("%v is wumbo sized, but "+ "lnd node isn't signalling wumbo", order.Details().Amt) } diff --git a/run.go b/run.go index 06ac44c..bcbc096 100644 --- a/run.go +++ b/run.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/signal" @@ -28,12 +29,14 @@ func Run(cfg *Config) error { } // Initialize logging at the default logging level. - err = logWriter.InitLogRotator( - filepath.Join(cfg.LogDir, DefaultLogFilename), - cfg.MaxLogFileSize, cfg.MaxLogFiles, - ) - if err != nil { - return err + if runtime.GOOS != "js" { + err := logWriter.InitLogRotator( + filepath.Join(cfg.LogDir, DefaultLogFilename), + cfg.MaxLogFileSize, cfg.MaxLogFiles, + ) + if err != nil { + return err + } } err = build.ParseAndSetDebugLevels(cfg.DebugLevel, logWriter) if err != nil { @@ -41,10 +44,21 @@ func Run(cfg *Config) error { } trader := NewServer(cfg) - err = trader.Start() + + if runtime.GOOS == "js" { + services, basicClient, err := getLnd(cfg.Network, cfg.Lnd) + if err != nil { + return fmt.Errorf("unable to connect to lnd: %v", err) + } + + err = trader.StartAsSubserver(basicClient, services) + } else { + err = trader.Start() + } if err != nil { return fmt.Errorf("unable to start server: %v", err) } + <-shutdownInterceptor.ShutdownChannel() return trader.Stop() } diff --git a/server.go b/server.go index 231ccbc..8362578 100644 --- a/server.go +++ b/server.go @@ -9,7 +9,6 @@ import ( "fmt" "net" "net/http" - "path" "strings" "sync" "sync/atomic" @@ -115,8 +114,14 @@ func (s *Server) Start() error { } }() + // Connect to lnd now. As there're some other lower-level operations we + // may need access to, we'll also make a connection for a + // "basic client". + // + // TODO(roasbeef): more granular macaroons, can ask user to make just + // what we need var err error - s.lndServices, err = getLnd(s.cfg.Network, s.cfg.Lnd) + s.lndServices, s.lndClient, err = getLnd(s.cfg.Network, s.cfg.Lnd) if err != nil { return err } @@ -125,20 +130,6 @@ func (s *Server) Start() error { return nil } - // As there're some other lower-level operations we may need access to, - // we'll also make a connection for a "basic client". - // - // TODO(roasbeef): more granular macaroons, can ask user to make just - // what we need - s.lndClient, err = lndclient.NewBasicClient( - s.cfg.Lnd.Host, s.cfg.Lnd.TLSPath, - path.Dir(s.cfg.Lnd.MacaroonPath), s.cfg.Network, - lndclient.MacFilename(path.Base(s.cfg.Lnd.MacaroonPath)), - ) - if err != nil { - return err - } - // Start the macaroon service and let it create its default macaroon in // case it doesn't exist yet. if err := s.startMacaroonService(); err != nil { @@ -590,41 +581,6 @@ func (s *Server) Stop() error { return nil } -// getLnd returns an instance of the lnd services proxy. -func getLnd(network string, cfg *LndConfig) (*lndclient.GrpcLndServices, error) { - // We'll want to wait for lnd to be fully synced to its chain backend. - // The call to NewLndServices will block until the sync is completed. - // But we still want to be able to shutdown the daemon if the user - // decides to not wait. For that we can pass down a context that we - // cancel on shutdown. - ctxc, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Make sure the context is canceled if the user requests shutdown. - go func() { - select { - // Client requests shutdown, cancel the wait. - case <-interceptor.ShutdownChannel(): - cancel() - - // The check was completed and the above defer canceled the - // context. We can just exit the goroutine, nothing more to do. - case <-ctxc.Done(): - } - }() - - return lndclient.NewLndServices(&lndclient.LndServicesConfig{ - LndAddress: cfg.Host, - Network: lndclient.Network(network), - CustomMacaroonPath: cfg.MacaroonPath, - TLSPath: cfg.TLSPath, - CheckVersion: minimalCompatibleVersion, - BlockUntilChainSynced: true, - BlockUntilUnlocked: true, - CallerCtx: ctxc, - }) -} - // Interceptor is the interface a client side gRPC interceptor has to implement. type Interceptor interface { // UnaryInterceptor intercepts normal, non-streaming requests from the diff --git a/wasm_exec.js b/wasm_exec.js new file mode 100644 index 0000000..82041e6 --- /dev/null +++ b/wasm_exec.js @@ -0,0 +1,626 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + // - Webpack + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + const fs = require("fs"); + if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { + global.fs = fs; + } + } + + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!global.process) { + global.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!global.crypto && global.require) { + const nodeCrypto = require("crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + if (!global.crypto) { + throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder && global.require) { + global.TextEncoder = require("util").TextEncoder; + } + if (!global.TextEncoder) { + throw new Error("global.TextEncoder is not available, polyfill required"); + } + + if (!global.TextDecoder && global.require) { + global.TextDecoder = require("util").TextDecoder; + } + if (!global.TextDecoder) { + throw new Error("global.TextDecoder is not available, polyfill required"); + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + global.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime1() (sec int64, nsec int32) + "runtime.walltime1": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [global, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + typeof module !== "undefined" && + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + go.argv = process.argv.slice(2); + go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); + go.exit = process.exit; + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})();