Skip to content

Commit

Permalink
client/core: check if wallet is synchronized for spend actions
Browse files Browse the repository at this point in the history
  • Loading branch information
ukane-philemon authored Jul 18, 2023
1 parent f1de923 commit a6ea571
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 19 deletions.
13 changes: 7 additions & 6 deletions client/core/bond.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,10 @@ func (c *Core) postRequiredBonds(dc *dexConnection, cfg *dexBondCfg, state *dexA
c.log.Errorf("failed to unlock bond asset wallet %v: %v", unbip(state.bondAssetID), err)
return
}
if !wallet.synchronized() {
c.log.Warnf("Wallet %v is not yet synchronized with the network. Cannot post new bonds yet.",
unbip(state.bondAssetID))
return // otherwise we might double spend if the wallet keys were used elsewhere
err = wallet.checkPeersAndSyncStatus()
if err != nil {
c.log.Errorf("Cannot post new bonds yet. %v", err)
return
}

// For the max bonded limit, we'll normalize all bonds to the
Expand Down Expand Up @@ -1075,8 +1075,9 @@ func (c *Core) PostBond(form *PostBondForm) (*PostBondResult, error) {
if _, ok := wallet.Wallet.(asset.Bonder); !ok { // will fail in MakeBondTx, but assert early
return nil, fmt.Errorf("wallet %v is not an asset.Bonder", bondAssetSymbol)
}
if !wallet.synchronized() { // otherwise we might double spend if the wallet keys were used elsewhere
return nil, fmt.Errorf("wallet %v is not synchronized", unbip(bondAssetID))
err = wallet.checkPeersAndSyncStatus()
if err != nil {
return nil, err
}

// Check the app password.
Expand Down
31 changes: 27 additions & 4 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -5324,6 +5324,10 @@ func (c *Core) Send(pw []byte, assetID uint32, value uint64, address string, sub
return nil, err
}

if err = wallet.checkPeersAndSyncStatus(); err != nil {
return nil, err
}

var coin asset.Coin
feeSuggestion := c.feeSuggestionAny(assetID)
if !subtract {
Expand Down Expand Up @@ -5379,6 +5383,11 @@ func (c *Core) ApproveToken(appPW []byte, assetID uint32, dexAddr string, onConf
return "", err
}

err = wallet.checkPeersAndSyncStatus()
if err != nil {
return "", err
}

dex, connected, err := c.dex(dexAddr)
if err != nil {
return "", err
Expand Down Expand Up @@ -5424,6 +5433,11 @@ func (c *Core) UnapproveToken(appPW []byte, assetID uint32, version uint32) (str
return "", err
}

err = wallet.checkPeersAndSyncStatus()
if err != nil {
return "", err
}

onConfirm := func() {
go c.notify(newTokenApprovalNote(wallet.state()))
}
Expand Down Expand Up @@ -10358,15 +10372,24 @@ func (c *Core) saveDisabledRateSources() {
}
}

func (c *Core) shieldedWallet(assetID uint32) (asset.ShieldedWallet, error) {
func (c *Core) shieldedWallet(assetID uint32, forFundTransfer ...bool) (asset.ShieldedWallet, error) {
w, found := c.wallet(assetID)
if !found {
return nil, fmt.Errorf("no %s wallet", unbip(assetID))
}

sw, is := w.Wallet.(asset.ShieldedWallet)
if !is {
return nil, fmt.Errorf("%s wallet is not a shielded wallet", unbip(assetID))
}

// Check if this wallet can send funds at the moment.
if len(forFundTransfer) > 0 && forFundTransfer[0] {
if err := w.checkPeersAndSyncStatus(); err != nil {
return nil, err
}
}

return sw, nil
}

Expand All @@ -10393,7 +10416,7 @@ func (c *Core) NewShieldedAddress(assetID uint32) (string, error) {

// ShieldFunds moves funds from the transparent account to the shielded account.
func (c *Core) ShieldFunds(assetID uint32, amt uint64) ([]byte, error) {
sw, err := c.shieldedWallet(assetID)
sw, err := c.shieldedWallet(assetID, true)
if err != nil {
return nil, err
}
Expand All @@ -10403,7 +10426,7 @@ func (c *Core) ShieldFunds(assetID uint32, amt uint64) ([]byte, error) {
// UnshieldFunds moves funds from the shielded account to the transparent
// account.
func (c *Core) UnshieldFunds(assetID uint32, amt uint64) ([]byte, error) {
sw, err := c.shieldedWallet(assetID)
sw, err := c.shieldedWallet(assetID, true)
if err != nil {
return nil, err
}
Expand All @@ -10419,7 +10442,7 @@ func (c *Core) SendShielded(appPW []byte, assetID uint32, toAddr string, amt uin
return nil, fmt.Errorf("password error: %w", err)
}

sw, err := c.shieldedWallet(assetID)
sw, err := c.shieldedWallet(assetID, true)
if err != nil {
return nil, err
}
Expand Down
7 changes: 7 additions & 0 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,13 @@ func TestSend(t *testing.T) {
if tWallet.sendFeeSuggestion != feeRate {
t.Fatalf("unexpected fee rate from FeeRater. wanted %d, got %d", feeRate, tWallet.sendFeeSuggestion)
}

// wallet is not synced
wallet.synced = false
_, err = tCore.Send(tPW, tUTXOAssetA.ID, 1e8, address, false)
if err == nil {
t.Fatalf("Expected error for a non-synchronized wallet")
}
}

func trade(t *testing.T, async bool) {
Expand Down
8 changes: 2 additions & 6 deletions client/core/trade.go
Original file line number Diff line number Diff line change
Expand Up @@ -2903,12 +2903,8 @@ func (t *trackedTrade) confirmRedemption(match *matchTracker) (bool, error) {
// In some cases the wallet will need to send a new redeem transaction.
toWallet := t.wallets.toWallet

if toWallet.peerCount < 1 {
return false, fmt.Errorf("%s wallet has no peers", unbip(toWallet.AssetID))
}

if !toWallet.synchronized() {
return false, fmt.Errorf("%s still syncing", unbip(toWallet.AssetID))
if err := toWallet.checkPeersAndSyncStatus(); err != nil {
return false, err
}

didUnlock, err := toWallet.refreshUnlock()
Expand Down
15 changes: 12 additions & 3 deletions client/core/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,20 @@ func (w *xcWallet) connected() bool {
return w.hookedUp
}

// synchronized is true if the wallet had been synchronized with the network.
func (w *xcWallet) synchronized() bool {
// checkPeersAndSyncStatus checks that the wallet is synced, and has peers
// otherwise we might double spend if the wallet keys were used elsewhere. This
// should be checked before attempting to send funds but does not replace any
// other checks that may be required.
func (w *xcWallet) checkPeersAndSyncStatus() error {
w.mtx.RLock()
defer w.mtx.RUnlock()
return w.synced
if w.peerCount < 1 {
return fmt.Errorf("%s wallet has no connected peers", unbip(w.AssetID))
}
if !w.synced {
return fmt.Errorf("%s wallet is not synchronized", unbip(w.AssetID))
}
return nil
}

// Connect calls the dex.Connector's Connect method, sets the xcWallet.hookedUp
Expand Down

0 comments on commit a6ea571

Please sign in to comment.