account: resume flow for accounts that have had updates applied
@@ -113,6 +113,13 @@ type Manager struct {
cfg ManagerConfig
watcher *watcher.Watcher
+ // watchMtx guards access to watchingExpiry.
+ watchMtx sync.Mutex
+
+ // watchingExpiry is the set of accounts we're currently tracking the
+ // expiration of.
+ watchingExpiry map[[33]byte]struct{}
+
wg sync.WaitGroup
quit chan struct{}
}
@@ -121,6 +128,7 @@ type Manager struct {
func NewManager(cfg *ManagerConfig) *Manager {
m := &Manager{
cfg: *cfg,
+ watchingExpiry: make(map[[33]byte]struct{}),
quit: make(chan struct{}),
}
@@ -483,8 +491,11 @@ func (m *Manager) locateTxByHash(ctx context.Context,
// handleStateOpen performs the necessary operations for accounts found in
// StateOpen.
func (m *Manager) handleStateOpen(ctx context.Context, account *Account) error {
- log.Infof("Watching account %x for spend and expiration",
- account.TraderKey.PubKey.SerializeCompressed())
+ var traderKey [33]byte
+ copy(traderKey[:], account.TraderKey.PubKey.SerializeCompressed())
+
+ log.Infof("Watching spend of %v for account %x", account.OutPoint,
+ traderKey)
accountOutput, err := account.Output()
if err != nil {
@@ -498,12 +509,20 @@ func (m *Manager) handleStateOpen(ctx context.Context, account *Account) error {
if err != nil {
return fmt.Errorf("unable to watch for spend: %v", err)
}
+
+ // Make sure we don't track the expiry again if we don't have to.
+ m.watchMtx.Lock()
+ if _, ok := m.watchingExpiry[traderKey]; !ok {
err = m.watcher.WatchAccountExpiration(
account.TraderKey.PubKey, account.Expiry,
)
if err != nil {
- return fmt.Errorf("unable to watch for expiration: %v", err)
+ m.watchMtx.Unlock()
+ return fmt.Errorf("unable to watch for expiration: %v",
+ err)
+ }
}
+ m.watchMtx.Unlock()
// Now that we have an open account, subscribe for updates to it to the
// server. We subscribe for the account instead of the individual orders
@@ -577,24 +596,24 @@ func (m *Manager) handleAccountSpend(traderKey *btcec.PublicKey,
// If the witness is for a multi-sig spend, then either an order by the
// trader was matched, or the account was closed. If it was closed, then
// the account output shouldn't have been recreated.
- //
- // TODO(wilmer): What do we do when an order matched and the account was
- // drained resulting in a dust amount? The output isn't recreated in
- // that case.
case clmscript.IsMultiSigSpend(spendWitness):
- // If the account output was recreated, then there's nothing
- // left for us to do. We'll defer updating the account here, as
- // we'll want to update it atomically along with the matched
- // order, which we don't have information of.
- nextAccountScript, err := account.NextOutputScript()
+ // An account cannot be spent without our knowledge, so we'll
+ // assume we always persist account updates before a broadcast
+ // of the spending transaction. Therefore, since we should
+ // already have the updates applied, we can just look for our
+ // current output in the transaction.
+ accountOutput, err := account.Output()
if err != nil {
return err
}
- _, ok := clmscript.LocateOutputScript(spendTx, nextAccountScript)
+ _, ok := clmscript.LocateOutputScript(
+ spendTx, accountOutput.PkScript,
+ )
if ok {
- // The account is still open so don't mark it as closed
- // below.
- return nil
+ // Proceed with the rest of the flow.
+ return m.resumeAccount(
+ context.Background(), account, false,
+ )
}
default:
