mirror of https://github.com/lightninglabs/loop
loopdb: make new loopdb package to house persistent storage
parent
74cf29a9cf
commit
cdcb9f8345
@ -1,96 +0,0 @@
|
|||||||
# Swaplet
|
|
||||||
|
|
||||||
## Uncharge swap (off -> on-chain)
|
|
||||||
|
|
||||||
```
|
|
||||||
swapcli uncharge 500
|
|
||||||
|
|
|
||||||
|
|
|
||||||
v
|
|
||||||
.-----------------------------.
|
|
||||||
| Swap CLI |
|
|
||||||
| ./cmd/swapcli |
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
| .-------------------. | .--------------. .---------------.
|
|
||||||
| | Swap Client (lib) | | | LND node | | Bitcoin node |
|
|
||||||
| | ./ |<-------------| |-------------------| |
|
|
||||||
| | | | | | on-chain | |
|
|
||||||
| | |------------->| | htlc | |
|
|
||||||
| | | | off-chain | | | |
|
|
||||||
| '-------------------' | htlc '--------------' '---------------'
|
|
||||||
'-----------------|-----------' | ^
|
|
||||||
| | |
|
|
||||||
| v |
|
|
||||||
| .--. .--.
|
|
||||||
| _ -( )- _ _ -( )- _
|
|
||||||
| .--,( ),--. .--,( ),--.
|
|
||||||
initiate| _.-( )-._ _.-( )-._
|
|
||||||
swap | ( LIGHTNING NETWORK ) ( BITCOIN NETWORK )
|
|
||||||
| '-._( )_.-' '-._( )_.-'
|
|
||||||
| '__,( ),__' '__,( ),__'
|
|
||||||
| - ._(__)_. - - ._(__)_. -
|
|
||||||
| | ^
|
|
||||||
| | |
|
|
||||||
v v |
|
|
||||||
.--------------------. off-chain .--------------. .---------------.
|
|
||||||
| Swap Server | htlc | LND node | | Bitcoin node |
|
|
||||||
| |<-------------| | | |
|
|
||||||
| | | | on-chain | |
|
|
||||||
| | | | htlc | |
|
|
||||||
| |--------------| |----------------->| |
|
|
||||||
| | | | | |
|
|
||||||
'--------------------' '--------------' '---------------'
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
LND and the swaplet are using go modules. Make sure that the `GO111MODULE` env variable is set to `on`.
|
|
||||||
|
|
||||||
In order to execute a swap, LND needs to be rebuilt with sub servers enabled.
|
|
||||||
|
|
||||||
### LND
|
|
||||||
|
|
||||||
* Checkout branch `master`
|
|
||||||
|
|
||||||
- `make install tags="signrpc walletrpc chainrpc"` to build and install lnd with required sub-servers enabled.
|
|
||||||
|
|
||||||
- Make sure there are no macaroons in the lnd dir `~/.lnd/data/chain/bitcoin/mainnet`. If there are, lnd has been started before and in that case, it could be that `admin.macaroon` doesn't contain signer permission. Delete `macaroons.db` and `*.macaroon`.
|
|
||||||
|
|
||||||
DO NOT DELETE `wallet.db` !
|
|
||||||
|
|
||||||
- Start lnd
|
|
||||||
|
|
||||||
### Swaplet
|
|
||||||
- `git clone git@gitlab.com:lightning-labs/swaplet.git`
|
|
||||||
- `cd swaplet/cmd`
|
|
||||||
- `go install ./...`
|
|
||||||
|
|
||||||
## Execute a swap
|
|
||||||
|
|
||||||
* Swaps are executed by a client daemon process. Run:
|
|
||||||
|
|
||||||
`swapd`
|
|
||||||
|
|
||||||
By default `swapd` attempts to connect to an lnd instance running on `localhost:10009` and reads the macaroon and tls certificate from `~/.lnd`. This can be altered using command line flags. See `swapd --help`.
|
|
||||||
|
|
||||||
`swapd` only listens on localhost and uses an unencrypted and unauthenticated connection.
|
|
||||||
|
|
||||||
* To initiate a swap, run:
|
|
||||||
|
|
||||||
`swapcli uncharge <amt_msat>`
|
|
||||||
|
|
||||||
When the swap is initiated successfully, `swapd` will see the process through.
|
|
||||||
|
|
||||||
* To query and track the swap status, run `swapcli` without arguments.
|
|
||||||
|
|
||||||
## Resume
|
|
||||||
When `swapd` is terminated (or killed) for whatever reason, it will pickup pending swaps after a restart.
|
|
||||||
|
|
||||||
Information about pending swaps is stored persistently in the swap database. Its location is `~/.swaplet/<network>/swapclient.db`.
|
|
||||||
|
|
||||||
## Multiple simultaneous swaps
|
|
||||||
|
|
||||||
It is possible to execute multiple swaps simultaneously.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// SwapStateType defines the types of swap states that exist. Every swap state
|
|
||||||
// defined as type SwapState above, falls into one of these SwapStateType
|
|
||||||
// categories.
|
|
||||||
type SwapStateType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// StateTypePending indicates that the swap is still pending.
|
|
||||||
StateTypePending SwapStateType = iota
|
|
||||||
|
|
||||||
// StateTypeSuccess indicates that the swap has completed successfully.
|
|
||||||
StateTypeSuccess
|
|
||||||
|
|
||||||
// StateTypeFail indicates that the swap has failed.
|
|
||||||
StateTypeFail
|
|
||||||
)
|
|
@ -1,65 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swapClientStore provides persistent storage for swaps.
|
|
||||||
type swapClientStore interface {
|
|
||||||
// getUnchargeSwaps returns all swaps currently in the store.
|
|
||||||
getUnchargeSwaps() ([]*PersistentUncharge, error)
|
|
||||||
|
|
||||||
// createUncharge adds an initiated swap to the store.
|
|
||||||
createUncharge(hash lntypes.Hash, swap *UnchargeContract) error
|
|
||||||
|
|
||||||
// updateUncharge stores a swap updateUncharge.
|
|
||||||
updateUncharge(hash lntypes.Hash, time time.Time, state SwapState) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// PersistentUnchargeEvent contains the dynamic data of a swap.
|
|
||||||
type PersistentUnchargeEvent struct {
|
|
||||||
State SwapState
|
|
||||||
Time time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// PersistentUncharge is a combination of the contract and the updates.
|
|
||||||
type PersistentUncharge struct {
|
|
||||||
Hash lntypes.Hash
|
|
||||||
|
|
||||||
Contract *UnchargeContract
|
|
||||||
Events []*PersistentUnchargeEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// State returns the most recent state of this swap.
|
|
||||||
func (s *PersistentUncharge) State() SwapState {
|
|
||||||
lastUpdate := s.LastUpdate()
|
|
||||||
if lastUpdate == nil {
|
|
||||||
return StateInitiated
|
|
||||||
}
|
|
||||||
|
|
||||||
return lastUpdate.State
|
|
||||||
}
|
|
||||||
|
|
||||||
// LastUpdate returns the most recent update of this swap.
|
|
||||||
func (s *PersistentUncharge) LastUpdate() *PersistentUnchargeEvent {
|
|
||||||
eventCount := len(s.Events)
|
|
||||||
|
|
||||||
if eventCount == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lastEvent := s.Events[eventCount-1]
|
|
||||||
return lastEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
// LastUpdateTime returns the last update time of this swap.
|
|
||||||
func (s *PersistentUncharge) LastUpdateTime() time.Time {
|
|
||||||
lastUpdate := s.LastUpdate()
|
|
||||||
if lastUpdate == nil {
|
|
||||||
return s.Contract.InitiationTime
|
|
||||||
}
|
|
||||||
|
|
||||||
return lastUpdate.Time
|
|
||||||
}
|
|
@ -0,0 +1,8 @@
|
|||||||
|
package loopdb
|
||||||
|
|
||||||
|
// itob returns an 8-byte big endian representation of v.
|
||||||
|
func itob(v uint64) []byte {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
byteOrder.PutUint64(b, v)
|
||||||
|
return b
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package loopdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SwapStore is the priamry database interface used by the loopd system. It
|
||||||
|
// houses informatino for all pending completed/failed swaps.
|
||||||
|
type SwapStore interface {
|
||||||
|
// FetchUnchargeSwaps returns all swaps currently in the store.
|
||||||
|
FetchUnchargeSwaps() ([]*PersistentUncharge, error)
|
||||||
|
|
||||||
|
// CreateUncharge adds an initiated swap to the store.
|
||||||
|
CreateUncharge(hash lntypes.Hash, swap *UnchargeContract) error
|
||||||
|
|
||||||
|
// UpdateUncharge stores a swap updateUncharge. This appends to the
|
||||||
|
// event log for a particular swap as it goes through the various
|
||||||
|
// stages in its lifetime.
|
||||||
|
UpdateUncharge(hash lntypes.Hash, time time.Time, state SwapState) error
|
||||||
|
|
||||||
|
// Close closes the underlying database.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): back up method in interface?
|
@ -0,0 +1,44 @@
|
|||||||
|
package loopdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/btcsuite/btclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// log is a logger that is initialized with no output filters. This means
|
||||||
|
// the package will not perform any logging by default until the caller
|
||||||
|
// requests it.
|
||||||
|
var log btclog.Logger
|
||||||
|
|
||||||
|
// The default amount of logging is none.
|
||||||
|
func init() {
|
||||||
|
DisableLog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableLog disables all library log output. Logging output is disabled
|
||||||
|
// by default until either UseLogger or SetLogWriter are called.
|
||||||
|
func DisableLog() {
|
||||||
|
log = btclog.Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseLogger uses a specified Logger to output package logging info.
|
||||||
|
// This should be used in preference to SetLogWriter if the caller is also
|
||||||
|
// using btclog.
|
||||||
|
func UseLogger(logger btclog.Logger) {
|
||||||
|
log = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// logClosure is used to provide a closure over expensive logging operations so
|
||||||
|
// don't have to be performed when the logging level doesn't warrant it.
|
||||||
|
type logClosure func() string
|
||||||
|
|
||||||
|
// String invokes the underlying function and returns the result.
|
||||||
|
func (c logClosure) String() string {
|
||||||
|
return c()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLogClosure returns a new closure over a function that returns a string
|
||||||
|
// which itself provides a Stringer interface so that it can be used with the
|
||||||
|
// logging system.
|
||||||
|
func newLogClosure(c func() string) logClosure {
|
||||||
|
return logClosure(c)
|
||||||
|
}
|
@ -0,0 +1,307 @@
|
|||||||
|
package loopdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/bbolt"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// dbFileName is the default file name of the client-side loop sub-swap
|
||||||
|
// database.
|
||||||
|
dbFileName = "loop.db"
|
||||||
|
|
||||||
|
// unchargeSwapsBucketKey is a bucket that contains all swaps that are
|
||||||
|
// currently pending or completed. This bucket is keyed by the
|
||||||
|
// swaphash, and leads to a nested sub-bucket that houses information
|
||||||
|
// for that swap.
|
||||||
|
//
|
||||||
|
// maps: swapHash -> swapBucket
|
||||||
|
unchargeSwapsBucketKey = []byte("uncharge-swaps")
|
||||||
|
|
||||||
|
// unchargeUpdatesBucketKey is a bucket that contains all updates
|
||||||
|
// pertaining to a swap. This is a sub-bucket of the swap bucket for a
|
||||||
|
// particular swap. This list only ever grows.
|
||||||
|
//
|
||||||
|
// path: unchargeUpdatesBucket -> swapBucket[hash] -> updateBucket
|
||||||
|
//
|
||||||
|
// maps: updateNumber -> time || state
|
||||||
|
updatesBucketKey = []byte("updates")
|
||||||
|
|
||||||
|
// contractKey is the key that stores the serialized swap contract. It
|
||||||
|
// is nested within the sub-bucket for each active swap.
|
||||||
|
//
|
||||||
|
// path: unchargeUpdatesBucket -> swapBucket[hash]
|
||||||
|
//
|
||||||
|
// value: time || rawSwapState
|
||||||
|
contractKey = []byte("contract")
|
||||||
|
|
||||||
|
byteOrder = binary.BigEndian
|
||||||
|
|
||||||
|
keyLength = 33
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileExists returns true if the file exists, and false otherwise.
|
||||||
|
func fileExists(path string) bool {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// boltSwapStore stores swap data in boltdb.
|
||||||
|
type boltSwapStore struct {
|
||||||
|
db *bbolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile-time flag to ensure that boltSwapStore implements the SwapStore
|
||||||
|
// interface.
|
||||||
|
var _ = (*boltSwapStore)(nil)
|
||||||
|
|
||||||
|
// newBoltSwapStore creates a new client swap store.
|
||||||
|
func newBoltSwapStore(dbPath string) (*boltSwapStore, error) {
|
||||||
|
|
||||||
|
// If the target path for the swap store doesn't exist, then we'll
|
||||||
|
// create it now before we proceed.
|
||||||
|
if !fileExists(dbPath) {
|
||||||
|
if err := os.MkdirAll(dbPath, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know that path exists, we'll open up bolt, which
|
||||||
|
// implements our default swap store.
|
||||||
|
path := filepath.Join(dbPath, dbFileName)
|
||||||
|
bdb, err := bbolt.Open(path, 0600, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll create all the buckets we need if this is the first time we're
|
||||||
|
// starting up. If they already exist, then these calls will be noops.
|
||||||
|
err = bdb.Update(func(tx *bbolt.Tx) error {
|
||||||
|
_, err := tx.CreateBucketIfNotExists(unchargeSwapsBucketKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tx.CreateBucketIfNotExists(updatesBucketKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tx.CreateBucketIfNotExists(metaBucket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, before we start, we'll sync the DB versions to pick up any
|
||||||
|
// possible DB migrations.
|
||||||
|
err = syncVersions(bdb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &boltSwapStore{
|
||||||
|
db: bdb,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchUnchargeSwaps returns all swaps currently in the store.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the loopdb.SwapStore interface.
|
||||||
|
func (s *boltSwapStore) FetchUnchargeSwaps() ([]*PersistentUncharge, error) {
|
||||||
|
var swaps []*PersistentUncharge
|
||||||
|
|
||||||
|
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
// First, we'll grab our main loop out swap bucket key.
|
||||||
|
rootBucket := tx.Bucket(unchargeSwapsBucketKey)
|
||||||
|
if rootBucket == nil {
|
||||||
|
return errors.New("bucket does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now traverse the root bucket for all active swaps. The
|
||||||
|
// primary key is the swap hash itself.
|
||||||
|
return rootBucket.ForEach(func(swapHash, v []byte) error {
|
||||||
|
// Only go into things that we know are sub-bucket
|
||||||
|
// keys.
|
||||||
|
if v != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the root bucket, we'll grab the next swap
|
||||||
|
// bucket for this swap from its swaphash.
|
||||||
|
swapBucket := rootBucket.Bucket(swapHash)
|
||||||
|
if swapBucket == nil {
|
||||||
|
return fmt.Errorf("swap bucket %x not found",
|
||||||
|
swapHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the main swap bucket obtained, we'll grab the
|
||||||
|
// raw swap contract bytes and decode it.
|
||||||
|
contractBytes := swapBucket.Get(contractKey)
|
||||||
|
if contractBytes == nil {
|
||||||
|
return errors.New("contract not found")
|
||||||
|
}
|
||||||
|
contract, err := deserializeUnchargeContract(
|
||||||
|
contractBytes,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we have the raw swap, we'll also need to decode
|
||||||
|
// each of the past updates to the swap itself.
|
||||||
|
stateBucket := swapBucket.Bucket(updatesBucketKey)
|
||||||
|
if stateBucket == nil {
|
||||||
|
return errors.New("updates bucket not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// De serialize and collect each swap update into our
|
||||||
|
// slice of swap events.
|
||||||
|
var updates []*PersistentUnchargeEvent
|
||||||
|
err = stateBucket.ForEach(func(k, v []byte) error {
|
||||||
|
event, err := deserializeUnchargeUpdate(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
updates = append(updates, event)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash lntypes.Hash
|
||||||
|
copy(hash[:], swapHash)
|
||||||
|
|
||||||
|
swap := PersistentUncharge{
|
||||||
|
Contract: contract,
|
||||||
|
Hash: hash,
|
||||||
|
Events: updates,
|
||||||
|
}
|
||||||
|
|
||||||
|
swaps = append(swaps, &swap)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return swaps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUncharge adds an initiated swap to the store.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the loopdb.SwapStore interface.
|
||||||
|
func (s *boltSwapStore) CreateUncharge(hash lntypes.Hash,
|
||||||
|
swap *UnchargeContract) error {
|
||||||
|
|
||||||
|
// If the hash doesn't match the pre-image, then this is an invalid
|
||||||
|
// swap so we'll bail out early.
|
||||||
|
if hash != swap.Preimage.Hash() {
|
||||||
|
return errors.New("hash and preimage do not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we'll create a new swap within the database.
|
||||||
|
return s.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
// First, we'll grab the root bucket that houses all of our
|
||||||
|
// main swaps.
|
||||||
|
rootBucket, err := tx.CreateBucketIfNotExists(
|
||||||
|
unchargeSwapsBucketKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the swap already exists, then we'll exit as we don't want
|
||||||
|
// to override a swap.
|
||||||
|
if rootBucket.Get(hash[:]) != nil {
|
||||||
|
return fmt.Errorf("swap %v already exists",
|
||||||
|
swap.Preimage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the root bucket, we'll make a new sub swap bucket using
|
||||||
|
// the swap hash.
|
||||||
|
swapBucket, err := rootBucket.CreateBucket(hash[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// With out swap bucket created, we'll serialize and store the
|
||||||
|
// swap itself.
|
||||||
|
contract, err := serializeUnchargeContract(swap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := swapBucket.Put(contractKey, contract); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we'll create an empty updates bucket for this swap
|
||||||
|
// to track any future updates to the swap itself.
|
||||||
|
_, err = swapBucket.CreateBucket(updatesBucketKey)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUncharge stores a swap updateUncharge. This appends to the event log
|
||||||
|
// for a particular swap as it goes through the various stages in its lifetime.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the loopdb.SwapStore interface.
|
||||||
|
func (s *boltSwapStore) UpdateUncharge(hash lntypes.Hash, time time.Time,
|
||||||
|
state SwapState) error {
|
||||||
|
|
||||||
|
return s.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
// Starting from the root bucket, we'll traverse the bucket
|
||||||
|
// hierarchy all the way down to the swap bucket, and the
|
||||||
|
// update sub-bucket within that.
|
||||||
|
rootBucket := tx.Bucket(unchargeSwapsBucketKey)
|
||||||
|
if rootBucket == nil {
|
||||||
|
return errors.New("bucket does not exist")
|
||||||
|
}
|
||||||
|
swapBucket := rootBucket.Bucket(hash[:])
|
||||||
|
if swapBucket == nil {
|
||||||
|
return errors.New("swap not found")
|
||||||
|
}
|
||||||
|
updateBucket := swapBucket.Bucket(updatesBucketKey)
|
||||||
|
if updateBucket == nil {
|
||||||
|
return errors.New("udpate bucket not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each update for this swap will get a new monotonically
|
||||||
|
// increasing ID number that we'll obtain now.
|
||||||
|
id, err := updateBucket.NextSequence()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the ID obtained, we'll write out this new update value.
|
||||||
|
updateValue, err := serializeUnchargeUpdate(time, state)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateBucket.Put(itob(id), updateValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the underlying database.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the loopdb.SwapStore interface.
|
||||||
|
func (s *boltSwapStore) Close() error {
|
||||||
|
return s.db.Close()
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package loopdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SwapContract contains the base data that is serialized to persistent storage
|
||||||
|
// for pending swaps.
|
||||||
|
type SwapContract struct {
|
||||||
|
Preimage lntypes.Preimage
|
||||||
|
AmountRequested btcutil.Amount
|
||||||
|
|
||||||
|
PrepayInvoice string
|
||||||
|
|
||||||
|
SenderKey [33]byte
|
||||||
|
ReceiverKey [33]byte
|
||||||
|
|
||||||
|
CltvExpiry int32
|
||||||
|
|
||||||
|
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
|
||||||
|
// paid for the prepayment to the server.
|
||||||
|
MaxPrepayRoutingFee btcutil.Amount
|
||||||
|
|
||||||
|
// MaxSwapFee is the maximum we are willing to pay the server for the
|
||||||
|
// swap.
|
||||||
|
MaxSwapFee btcutil.Amount
|
||||||
|
|
||||||
|
// MaxMinerFee is the maximum in on-chain fees that we are willing to
|
||||||
|
// spend.
|
||||||
|
MaxMinerFee btcutil.Amount
|
||||||
|
|
||||||
|
// InitiationHeight is the block height at which the swap was
|
||||||
|
// initiated.
|
||||||
|
InitiationHeight int32
|
||||||
|
|
||||||
|
// InitiationTime is the time at which the swap was initiated.
|
||||||
|
InitiationTime time.Time
|
||||||
|
}
|
Loading…
Reference in New Issue