Merge pull request #736 from bottlepay/wtxmgr-leases
wallet: list leases and parameterize duration
This commit is contained in:
commit
4ec908df93
3 changed files with 59 additions and 16 deletions
|
@ -2659,6 +2659,19 @@ func (w *Wallet) ListUnspent(minconf, maxconf int32,
|
||||||
return results, err
|
return results, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListLeasedOutputs returns a list of objects representing the currently locked
|
||||||
|
// utxos.
|
||||||
|
func (w *Wallet) ListLeasedOutputs() ([]*wtxmgr.LockedOutput, error) {
|
||||||
|
var outputs []*wtxmgr.LockedOutput
|
||||||
|
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||||
|
ns := tx.ReadBucket(wtxmgrNamespaceKey)
|
||||||
|
var err error
|
||||||
|
outputs, err = w.TxStore.ListLockedOutputs(ns)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return outputs, err
|
||||||
|
}
|
||||||
|
|
||||||
// DumpPrivKeys returns the WIF-encoded private keys for all addresses with
|
// DumpPrivKeys returns the WIF-encoded private keys for all addresses with
|
||||||
// private keys in a wallet.
|
// private keys in a wallet.
|
||||||
func (w *Wallet) DumpPrivKeys() ([]string, error) {
|
func (w *Wallet) DumpPrivKeys() ([]string, error) {
|
||||||
|
@ -2895,12 +2908,14 @@ func (w *Wallet) LockedOutpoints() []btcjson.TransactionInput {
|
||||||
//
|
//
|
||||||
// NOTE: This differs from LockOutpoint in that outputs are locked for a limited
|
// NOTE: This differs from LockOutpoint in that outputs are locked for a limited
|
||||||
// amount of time and their locks are persisted to disk.
|
// amount of time and their locks are persisted to disk.
|
||||||
func (w *Wallet) LeaseOutput(id wtxmgr.LockID, op wire.OutPoint) (time.Time, error) {
|
func (w *Wallet) LeaseOutput(id wtxmgr.LockID, op wire.OutPoint,
|
||||||
|
duration time.Duration) (time.Time, error) {
|
||||||
|
|
||||||
var expiry time.Time
|
var expiry time.Time
|
||||||
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(wtxmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(wtxmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
expiry, err = w.TxStore.LockOutput(ns, id, op)
|
expiry, err = w.TxStore.LockOutput(ns, id, op, duration)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return expiry, err
|
return expiry, err
|
||||||
|
|
37
wtxmgr/tx.go
37
wtxmgr/tx.go
|
@ -24,9 +24,6 @@ import (
|
||||||
const (
|
const (
|
||||||
// TxLabelLimit is the length limit we impose on transaction labels.
|
// TxLabelLimit is the length limit we impose on transaction labels.
|
||||||
TxLabelLimit = 500
|
TxLabelLimit = 500
|
||||||
|
|
||||||
// DefaultLockDuration is the default duration used to lock outputs.
|
|
||||||
DefaultLockDuration = 10 * time.Minute
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -125,6 +122,14 @@ type TxRecord struct {
|
||||||
SerializedTx []byte // Optional: may be nil
|
SerializedTx []byte // Optional: may be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LockedOutput is a type that contains an outpoint of an UTXO and its lock
|
||||||
|
// lease information.
|
||||||
|
type LockedOutput struct {
|
||||||
|
Outpoint wire.OutPoint
|
||||||
|
LockID LockID
|
||||||
|
Expiration time.Time
|
||||||
|
}
|
||||||
|
|
||||||
// NewTxRecord creates a new transaction record that may be inserted into the
|
// NewTxRecord creates a new transaction record that may be inserted into the
|
||||||
// store. It uses memoization to save the transaction hash and the serialized
|
// store. It uses memoization to save the transaction hash and the serialized
|
||||||
// transaction.
|
// transaction.
|
||||||
|
@ -1154,7 +1159,7 @@ func isKnownOutput(ns walletdb.ReadWriteBucket, op wire.OutPoint) bool {
|
||||||
// already been locked to a different ID, then ErrOutputAlreadyLocked is
|
// already been locked to a different ID, then ErrOutputAlreadyLocked is
|
||||||
// returned.
|
// returned.
|
||||||
func (s *Store) LockOutput(ns walletdb.ReadWriteBucket, id LockID,
|
func (s *Store) LockOutput(ns walletdb.ReadWriteBucket, id LockID,
|
||||||
op wire.OutPoint) (time.Time, error) {
|
op wire.OutPoint, duration time.Duration) (time.Time, error) {
|
||||||
|
|
||||||
// Make sure the output is known.
|
// Make sure the output is known.
|
||||||
if !isKnownOutput(ns, op) {
|
if !isKnownOutput(ns, op) {
|
||||||
|
@ -1167,7 +1172,7 @@ func (s *Store) LockOutput(ns walletdb.ReadWriteBucket, id LockID,
|
||||||
return time.Time{}, ErrOutputAlreadyLocked
|
return time.Time{}, ErrOutputAlreadyLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
expiry := s.clock.Now().Add(DefaultLockDuration)
|
expiry := s.clock.Now().Add(duration)
|
||||||
if err := lockOutput(ns, id, op, expiry); err != nil {
|
if err := lockOutput(ns, id, op, expiry); err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
}
|
}
|
||||||
|
@ -1226,3 +1231,25 @@ func (s *Store) DeleteExpiredLockedOutputs(ns walletdb.ReadWriteBucket) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListLockedOutputs returns a list of objects representing the currently locked
|
||||||
|
// utxos.
|
||||||
|
func (s *Store) ListLockedOutputs(ns walletdb.ReadBucket) ([]*LockedOutput,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
var outputs []*LockedOutput
|
||||||
|
err := forEachLockedOutput(
|
||||||
|
ns, func(op wire.OutPoint, id LockID, expiration time.Time) {
|
||||||
|
outputs = append(outputs, &LockedOutput{
|
||||||
|
Outpoint: op,
|
||||||
|
LockID: id,
|
||||||
|
Expiration: expiration,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2425,19 +2425,20 @@ func assertOutputLocksExist(t *testing.T, s *Store, ns walletdb.ReadBucket,
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var found []wire.OutPoint
|
outputs, err := s.ListLockedOutputs(ns)
|
||||||
forEachLockedOutput(ns, func(op wire.OutPoint, _ LockID, _ time.Time) {
|
if err != nil {
|
||||||
found = append(found, op)
|
t.Fatal(err)
|
||||||
})
|
}
|
||||||
if len(found) != len(exp) {
|
|
||||||
|
if len(outputs) != len(exp) {
|
||||||
t.Fatalf("expected to find %v locked output(s), found %v",
|
t.Fatalf("expected to find %v locked output(s), found %v",
|
||||||
len(exp), len(found))
|
len(exp), len(outputs))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, expOp := range exp {
|
for _, expOp := range exp {
|
||||||
exists := false
|
exists := false
|
||||||
for _, foundOp := range found {
|
for _, found := range outputs {
|
||||||
if expOp == foundOp {
|
if expOp == found.Outpoint {
|
||||||
exists = true
|
exists = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -2453,7 +2454,7 @@ func lock(t *testing.T, s *Store, ns walletdb.ReadWriteBucket,
|
||||||
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
expiry, err := s.LockOutput(ns, id, op)
|
expiry, err := s.LockOutput(ns, id, op, 10*time.Minute)
|
||||||
if err != exp {
|
if err != exp {
|
||||||
t.Fatalf("expected err %q, got %q", exp, err)
|
t.Fatalf("expected err %q, got %q", exp, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue