diff --git a/wallet/wallet.go b/wallet/wallet.go index e4d7172..079fda0 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -2659,6 +2659,19 @@ func (w *Wallet) ListUnspent(minconf, maxconf int32, 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 // private keys in a wallet. 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 // 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 err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(wtxmgrNamespaceKey) var err error - expiry, err = w.TxStore.LockOutput(ns, id, op) + expiry, err = w.TxStore.LockOutput(ns, id, op, duration) return err }) return expiry, err diff --git a/wtxmgr/tx.go b/wtxmgr/tx.go index 9ef7329..19c5fc2 100644 --- a/wtxmgr/tx.go +++ b/wtxmgr/tx.go @@ -24,9 +24,6 @@ import ( const ( // TxLabelLimit is the length limit we impose on transaction labels. TxLabelLimit = 500 - - // DefaultLockDuration is the default duration used to lock outputs. - DefaultLockDuration = 10 * time.Minute ) var ( @@ -125,6 +122,14 @@ type TxRecord struct { 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 // store. It uses memoization to save the transaction hash and the serialized // transaction. @@ -1154,7 +1159,7 @@ func isKnownOutput(ns walletdb.ReadWriteBucket, op wire.OutPoint) bool { // already been locked to a different ID, then ErrOutputAlreadyLocked is // returned. 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. if !isKnownOutput(ns, op) { @@ -1167,7 +1172,7 @@ func (s *Store) LockOutput(ns walletdb.ReadWriteBucket, id LockID, 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 { return time.Time{}, err } @@ -1226,3 +1231,25 @@ func (s *Store) DeleteExpiredLockedOutputs(ns walletdb.ReadWriteBucket) error { 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 +} diff --git a/wtxmgr/tx_test.go b/wtxmgr/tx_test.go index 344b905..9434845 100644 --- a/wtxmgr/tx_test.go +++ b/wtxmgr/tx_test.go @@ -2425,19 +2425,20 @@ func assertOutputLocksExist(t *testing.T, s *Store, ns walletdb.ReadBucket, t.Helper() - var found []wire.OutPoint - forEachLockedOutput(ns, func(op wire.OutPoint, _ LockID, _ time.Time) { - found = append(found, op) - }) - if len(found) != len(exp) { + outputs, err := s.ListLockedOutputs(ns) + if err != nil { + t.Fatal(err) + } + + if len(outputs) != len(exp) { t.Fatalf("expected to find %v locked output(s), found %v", - len(exp), len(found)) + len(exp), len(outputs)) } for _, expOp := range exp { exists := false - for _, foundOp := range found { - if expOp == foundOp { + for _, found := range outputs { + if expOp == found.Outpoint { exists = true break } @@ -2453,7 +2454,7 @@ func lock(t *testing.T, s *Store, ns walletdb.ReadWriteBucket, t.Helper() - expiry, err := s.LockOutput(ns, id, op) + expiry, err := s.LockOutput(ns, id, op, 10*time.Minute) if err != exp { t.Fatalf("expected err %q, got %q", exp, err) }