Skip to content

Commit

Permalink
Merge branch 'watchonlytests'
Browse files Browse the repository at this point in the history
  • Loading branch information
benma committed Dec 5, 2023
2 parents 22a4003 + 5e53dbe commit 6817b4a
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 98 deletions.
228 changes: 164 additions & 64 deletions backend/accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ func checkShownAccountsLen(t *testing.T, b *Backend, expectedLoaded int, expecte
require.Equal(t, expectedPersisted, cntPersisted)
}

// A keystore with a similar config to a BitBox02 - supporting unified and multiple accounts, no
// legacy P2PKH.
func makeBitbox02LikeKeystore() *keystoremock.KeystoreMock {
// A keystore with a similar config to a BitBox02 Multi - supporting unified and multiple accounts,
// no legacy P2PKH.
func makeBitBox02Multi() *keystoremock.KeystoreMock {
fingerprint := []byte{0x55, 0x055, 0x55, 0x55}
// From mnemonic: wisdom minute home employ west tail liquid mad deal catalog narrow mistake
rootKey := mustXKey("xprv9s21ZrQH143K3gie3VFLgx8JcmqZNsBcBc6vAdJrsf4bPRhx69U8qZe3EYAyvRWyQdEfz7ZpyYtL8jW2d2Lfkfh6g2zivq8JdZPQqxoxLwB")
Expand Down Expand Up @@ -122,6 +122,22 @@ func makeBitbox02LikeKeystore() *keystoremock.KeystoreMock {
}
}

// A keystore with a similar config to a BitBox02 Bitcon-only - supporting unified and multiple
// accounts, no legacy P2PKH.
func makeBitBox02BTCOnly() *keystoremock.KeystoreMock {
ks := makeBitBox02Multi()
ks.SupportsAccountFunc = func(coin coinpkg.Coin, meta interface{}) bool {
switch coin.(type) {
case *btc.Coin:
scriptType := meta.(signing.ScriptType)
return coin.Code() == coinpkg.CodeBTC && scriptType != signing.ScriptTypeP2PKH
default:
return false
}
}
return ks
}

func TestSortAccounts(t *testing.T) {
xpub, err := hdkeychain.NewMaster(make([]byte, 32), &chaincfg.TestNet3Params)
require.NoError(t, err)
Expand Down Expand Up @@ -535,7 +551,7 @@ func TestSupportedCoins(t *testing.T) {
}

func TestCreateAndPersistAccountConfig(t *testing.T) {
bitbox02LikeKeystore := makeBitbox02LikeKeystore()
bitbox02LikeKeystore := makeBitBox02Multi()

fingerprint := []byte{0x55, 0x055, 0x55, 0x55}
// From mnemonic: wisdom minute home employ west tail liquid mad deal catalog narrow mistake
Expand Down Expand Up @@ -1012,59 +1028,8 @@ func TestCreateAndAddAccount(t *testing.T) {
// The second point is important because it's possible to use e.g. a BitBox02-Multi and a
// Bitbox02-btconly with the same seed, so we shouldn't load all persisted accounts without checking.
func TestAccountSupported(t *testing.T) {
// From mnemonic: wisdom minute home employ west tail liquid mad deal catalog narrow mistake
rootKey := mustXKey("xprv9s21ZrQH143K3gie3VFLgx8JcmqZNsBcBc6vAdJrsf4bPRhx69U8qZe3EYAyvRWyQdEfz7ZpyYtL8jW2d2Lfkfh6g2zivq8JdZPQqxoxLwB")
keystoreHelper := software.NewKeystore(rootKey)

fingerprint := []byte{0x55, 0x055, 0x55, 0x55}
bb02Multi := &keystoremock.KeystoreMock{
NameFunc: func() (string, error) {
return "Mock multi", nil
},
RootFingerprintFunc: func() ([]byte, error) {
return fingerprint, nil
},
SupportsAccountFunc: func(coin coinpkg.Coin, meta interface{}) bool {
switch coin.(type) {
case *btc.Coin:
scriptType := meta.(signing.ScriptType)
return scriptType != signing.ScriptTypeP2PKH
default:
return true
}
},
SupportsUnifiedAccountsFunc: func() bool {
return true
},
SupportsMultipleAccountsFunc: func() bool {
return true
},
ExtendedPublicKeyFunc: keystoreHelper.ExtendedPublicKey,
}
bb02BtcOnly := &keystoremock.KeystoreMock{
NameFunc: func() (string, error) {
return "Mock btconly", nil
},
RootFingerprintFunc: func() ([]byte, error) {
return fingerprint, nil
},
SupportsAccountFunc: func(coin coinpkg.Coin, meta interface{}) bool {
switch coin.(type) {
case *btc.Coin:
scriptType := meta.(signing.ScriptType)
return coin.Code() == coinpkg.CodeBTC && scriptType != signing.ScriptTypeP2PKH
default:
return false
}
},
SupportsUnifiedAccountsFunc: func() bool {
return true
},
SupportsMultipleAccountsFunc: func() bool {
return true
},
ExtendedPublicKeyFunc: keystoreHelper.ExtendedPublicKey,
}
bb02Multi := makeBitBox02Multi()
bb02BtcOnly := makeBitBox02BTCOnly()

b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
Expand All @@ -1073,8 +1038,7 @@ func TestAccountSupported(t *testing.T) {

// Registering a new keystore persists a set of initial default accounts.
b.registerKeystore(bb02Multi)
require.Len(t, b.Accounts(), 3)
require.Len(t, b.Config().AccountsConfig().Accounts, 3)
checkShownAccountsLen(t, b, 3, 3)

b.DeregisterKeystore()
// Registering a Bitcoin-only like keystore loads also the altcoins that were persisted
Expand All @@ -1093,14 +1057,14 @@ func TestAccountSupported(t *testing.T) {
}

func TestInactiveAccount(t *testing.T) {
bitbox02LikeKeystore := makeBitbox02LikeKeystore()
bitbox02LikeKeystore := makeBitBox02Multi()
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()

// 1) Registering a new keystore persists a set of initial default accounts.
b.registerKeystore(bitbox02LikeKeystore)
require.Len(t, b.Accounts(), 3)
require.Len(t, b.Config().AccountsConfig().Accounts, 3)

checkShownAccountsLen(t, b, 3, 3)
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-btc-0"))
require.False(t, b.Config().AccountsConfig().Lookup("v0-55555555-btc-0").Inactive)
require.True(t, !lookup(b.Accounts(), "v0-55555555-btc-0").Config().Config.Inactive)
Expand Down Expand Up @@ -1264,7 +1228,7 @@ func TestRenameAccount(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()

b.registerKeystore(makeBitbox02LikeKeystore())
b.registerKeystore(makeBitBox02Multi())

require.NoError(t, b.RenameAccount("v0-55555555-btc-0", "renamed"))
require.Equal(t, "renamed", b.accounts.lookup("v0-55555555-btc-0").Config().Config.Name)
Expand All @@ -1275,7 +1239,7 @@ func TestMaybeAddHiddenUnusedAccounts(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()

b.registerKeystore(makeBitbox02LikeKeystore())
b.registerKeystore(makeBitBox02Multi())

// Initial accounts added: Bitcoin, Litecoin, Ethereum.
checkShownAccountsLen(t, b, 3, 3)
Expand Down Expand Up @@ -1319,3 +1283,139 @@ func TestMaybeAddHiddenUnusedAccounts(t *testing.T) {
require.Len(t, b.config.AccountsConfig().Accounts, 14)
require.NotNil(t, b.config.AccountsConfig().Lookup("v0-55555555-btc-6"))
}

func TestWatchonly(t *testing.T) {
filterAcct := func(code accountsTypes.Code) func(acct *config.Account) bool {
return func(acct *config.Account) bool {
return acct.Code == code
}
}

// No watchonly - accounts are loaded when registering keystore and unloaded when deregistering
// keystore.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)
b.DeregisterKeystore()
checkShownAccountsLen(t, b, 0, 3)
})

// Watchonly enabled before keystore is registered - all loaded accounts thereafter become
// watched.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
require.NoError(t, b.SetWatchonly(true))
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)
b.DeregisterKeystore()
// Accounts remain loaded.
checkShownAccountsLen(t, b, 3, 3)
})

// Watchonly enabled while keystore is registered - all already loaded accounts become watched.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)
require.NoError(t, b.SetWatchonly(true))
b.DeregisterKeystore()
// Accounts remain loaded.
checkShownAccountsLen(t, b, 3, 3)
})

// A specific account is excluded from watchonly, then re-included.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
require.NoError(t, b.SetWatchonly(true))
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)
exclude := accountsTypes.Code("v0-55555555-btc-0")
require.NotNil(t, lookup(b.Accounts(), exclude))
_false := false
require.NoError(t, b.AccountSetWatch(filterAcct(exclude), &_false))
b.DeregisterKeystore()
// Accounts remain loaded except one.
checkShownAccountsLen(t, b, 2, 3)
require.Nil(t, lookup(b.Accounts(), exclude))

// Re-watch that account. In the UI this is possible as the edit dialog remains open after
// disabling watchonly.
_true := true
require.NoError(t, b.AccountSetWatch(filterAcct(exclude), &_true))
checkShownAccountsLen(t, b, 3, 3)
require.NotNil(t, lookup(b.Accounts(), exclude))
})

// Watchonly is disabled while some watched accounts are shown with no keystore connected. All
// accounts should disappear. When re-enabling watchonly, they do not reappear - connecting the
// keystore again is necessary.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
require.NoError(t, b.SetWatchonly(true))
b.registerKeystore(makeBitBox02Multi())
b.DeregisterKeystore()
// Accounts remain loaded.
checkShownAccountsLen(t, b, 3, 3)

// Disable watchonly, all accounts disappear.
require.NoError(t, b.SetWatchonly(false))
checkShownAccountsLen(t, b, 0, 3)

// Re-enable watchonly - accounts do not show up yet.
require.NoError(t, b.SetWatchonly(true))
checkShownAccountsLen(t, b, 0, 3)

// Reconnecting the keystore brings back the watched accounts.
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)
b.DeregisterKeystore()
checkShownAccountsLen(t, b, 3, 3)
})

// Disable global watchonly while keystore is connected does not make the accounts disappear
// yet. They only disappear once the keytore is disconnected.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
require.NoError(t, b.SetWatchonly(true))
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)

// Disable watchonly, all accounts remain as the keystore is still connected.
require.NoError(t, b.SetWatchonly(false))
checkShownAccountsLen(t, b, 3, 3)

// Accounts disappear when the keystore is disconnected.
b.DeregisterKeystore()
checkShownAccountsLen(t, b, 0, 3)
})

// Disable watchonly for a specific account while keystore is connected does not make the
// account disappear yet. It only disappears once the keytore is disconnected.
t.Run("", func(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
require.NoError(t, b.SetWatchonly(true))
b.registerKeystore(makeBitBox02Multi())
checkShownAccountsLen(t, b, 3, 3)

exclude := accountsTypes.Code("v0-55555555-btc-0")
require.NotNil(t, lookup(b.Accounts(), exclude))
_false := false
require.NoError(t, b.AccountSetWatch(filterAcct(exclude), &_false))

// Account remains loaded as the keystore is still connected.
checkShownAccountsLen(t, b, 3, 3)

// Disconnecting the keystore makes the one account disappear that is not being watched.
b.DeregisterKeystore()
checkShownAccountsLen(t, b, 2, 3)
require.Nil(t, lookup(b.Accounts(), exclude))
})
}
38 changes: 4 additions & 34 deletions backend/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,12 @@ func TestRegisterKeystore(t *testing.T) {
b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()

require.Len(t, b.Accounts(), 0)
require.Len(t, b.Config().AccountsConfig().Accounts, 0)
checkShownAccountsLen(t, b, 0, 0)

// Registering a new keystore persists a set of initial default accounts and the keystore.
b.registerKeystore(ks1)
require.Equal(t, ks1, b.Keystore())
require.Len(t, b.Accounts(), 3)
require.Len(t, b.Config().AccountsConfig().Accounts, 3)
checkShownAccountsLen(t, b, 3, 3)
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-btc-0"))
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-ltc-0"))
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-eth-0"))
Expand Down Expand Up @@ -198,34 +196,7 @@ func lookup(accts []accounts.Interface, code accountsTypes.Code) accounts.Interf
// 6) Deactivate an account.
// 7) Rename an inactive account.
func TestAccounts(t *testing.T) {
// From mnemonic: wisdom minute home employ west tail liquid mad deal catalog narrow mistake
rootKey := mustXKey("xprv9s21ZrQH143K3gie3VFLgx8JcmqZNsBcBc6vAdJrsf4bPRhx69U8qZe3EYAyvRWyQdEfz7ZpyYtL8jW2d2Lfkfh6g2zivq8JdZPQqxoxLwB")
keystoreHelper := software.NewKeystore(rootKey)

ks := &keystoremock.KeystoreMock{
NameFunc: func() (string, error) {
return "Mock keystore", nil
},
RootFingerprintFunc: func() ([]byte, error) {
return []byte{0x55, 0x055, 0x55, 0x55}, nil
},
SupportsAccountFunc: func(coin coinpkg.Coin, meta interface{}) bool {
switch coin.(type) {
case *btc.Coin:
scriptType := meta.(signing.ScriptType)
return scriptType != signing.ScriptTypeP2PKH
default:
return true
}
},
SupportsUnifiedAccountsFunc: func() bool {
return true
},
SupportsMultipleAccountsFunc: func() bool {
return true
},
ExtendedPublicKeyFunc: keystoreHelper.ExtendedPublicKey,
}
ks := makeBitBox02Multi()

b := newBackend(t, testnetDisabled, regtestDisabled)
defer b.Close()
Expand All @@ -235,8 +206,7 @@ func TestAccounts(t *testing.T) {

// 1) Registering a new keystore persists a set of initial default accounts.
b.registerKeystore(ks)
require.Len(t, b.Accounts(), 3)
require.Len(t, b.Config().AccountsConfig().Accounts, 3)
checkShownAccountsLen(t, b, 3, 3)
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-btc-0"))
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-ltc-0"))
require.NotNil(t, b.Config().AccountsConfig().Lookup("v0-55555555-eth-0"))
Expand Down

0 comments on commit 6817b4a

Please sign in to comment.