mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 14:33:06 +01:00
2020 06 14 wallet root accounts (#1556)
* Implement unit test cases for initializing a wallet twice, and failing with an exception when we initialize a wallet with a different key-manager * Use bip39PasswordOpt cached in the WalletUnitTest suite * Turn off logging * Run scalafmt * Turn log level back to WARN * Run scalafmt
This commit is contained in:
parent
914c905bd7
commit
ba0f38ccf6
4 changed files with 62 additions and 16 deletions
|
@ -418,7 +418,6 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
extraConfig: Option[Config] = None)(
|
||||
implicit config: BitcoinSAppConfig,
|
||||
ec: ExecutionContext): Future[Wallet] = {
|
||||
|
||||
val newWalletConf = extraConfig match {
|
||||
case None =>
|
||||
config.walletConf
|
||||
|
|
|
@ -20,11 +20,12 @@ import org.scalatest.compatible.Assertion
|
|||
import scala.concurrent.Future
|
||||
|
||||
class WalletUnitTest extends BitcoinSWalletTest {
|
||||
private val bip39PasswordOpt: Option[String] = getBIP39PasswordOpt()
|
||||
|
||||
override type FixtureParam = WalletApi
|
||||
override type FixtureParam = Wallet
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withNewWallet(test, getBIP39PasswordOpt())
|
||||
withNewWallet(test, bip39PasswordOpt)
|
||||
|
||||
behavior of "Wallet - unit test"
|
||||
|
||||
|
@ -166,4 +167,26 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
blockHeight = 1)) == matched)
|
||||
}
|
||||
}
|
||||
|
||||
it must "be able to call initialize twice without throwing an exception if we have the same key manager" in {
|
||||
wallet: Wallet =>
|
||||
val twiceF = Wallet.initialize(wallet, bip39PasswordOpt).flatMap { _ =>
|
||||
Wallet.initialize(wallet, bip39PasswordOpt)
|
||||
}
|
||||
|
||||
twiceF.map(_ => succeed)
|
||||
|
||||
}
|
||||
|
||||
it must "be able to detect an incompatible key manager with a wallet" in {
|
||||
wallet: Wallet =>
|
||||
recoverToSucceededIf[RuntimeException] {
|
||||
Wallet.initialize(wallet, bip39PasswordOpt).flatMap { _ =>
|
||||
//use a BIP39 password to make the key-managers different
|
||||
Wallet.initialize(
|
||||
wallet,
|
||||
Some("random-password-to-make-key-managers-different"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -440,7 +440,7 @@ object Wallet extends WalletLogger {
|
|||
WalletImpl(keyManager, nodeApi, chainQueryApi, feeRateApi, creationTime)
|
||||
}
|
||||
|
||||
/** Creates the level 0 account for the given HD purpose */
|
||||
/** Creates the level 0 account for the given HD purpose, if the root account exists do nothing */
|
||||
private def createRootAccount(wallet: Wallet, keyManager: BIP39KeyManager)(
|
||||
implicit walletAppConfig: WalletAppConfig,
|
||||
ec: ExecutionContext): Future[AccountDb] = {
|
||||
|
@ -451,15 +451,35 @@ object Wallet extends WalletLogger {
|
|||
// safe since we're deriving from a priv
|
||||
val xpub = keyManager.deriveXPub(account).get
|
||||
val accountDb = AccountDb(xpub, account)
|
||||
logger.debug(
|
||||
s"Creating account with constant prefix ${keyManager.kmParams.purpose}")
|
||||
wallet.accountDAO
|
||||
.create(accountDb)
|
||||
.map { written =>
|
||||
logger.debug(
|
||||
s"Saved account with constant prefix ${keyManager.kmParams.purpose} to DB")
|
||||
written
|
||||
}
|
||||
val accountDAO = wallet.accountDAO
|
||||
|
||||
//see if we already have this account in our database
|
||||
//Three possible cases:
|
||||
//1. We have nothing in our database, so we need to insert it
|
||||
//2. We already have this account in our database, so we do nothing
|
||||
//3. We have this account in our database, with a DIFFERENT xpub. This is bad. Fail with an exception
|
||||
// this most likely means that we have a different key manager than we expected
|
||||
val accountOptF = accountDAO.read(account.coin, account.index)
|
||||
accountOptF.flatMap {
|
||||
case Some(account) =>
|
||||
if (account.xpub != xpub) {
|
||||
val errorMsg = s"Divergent xpubs for account=${account}. Existing database xpub=${account.xpub}, new xpub=${xpub}. " +
|
||||
s"It is possible we have a different key manager being used than expected, keymanager=${keyManager}"
|
||||
Future.failed(new RuntimeException(errorMsg))
|
||||
} else {
|
||||
logger.debug(
|
||||
s"Account already exists in database, no need to create it, account=${account}")
|
||||
Future.successful(account)
|
||||
}
|
||||
case None =>
|
||||
wallet.accountDAO
|
||||
.create(accountDb)
|
||||
.map { written =>
|
||||
logger.info(s"Created account=${accountDb} to DB")
|
||||
written
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def initialize(wallet: Wallet, bip39PasswordOpt: Option[String])(
|
||||
|
@ -485,8 +505,9 @@ object Wallet extends WalletLogger {
|
|||
case Left(err) =>
|
||||
//probably means you haven't initialized the key manager via the
|
||||
//'CreateKeyManagerApi'
|
||||
throw new RuntimeException(
|
||||
s"Failed to create keymanager with params=$kmParams err=$err")
|
||||
Future.failed(
|
||||
new RuntimeException(
|
||||
s"Failed to create keymanager with params=$kmParams err=$err"))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ import org.bitcoins.keymanager.util.HDUtil
|
|||
|
||||
/** Represents the xpub at the account level, NOT the root xpub
|
||||
* that in conjunction with the path specified in hdAccount
|
||||
* can be used to generate the account level xpub */
|
||||
* can be used to generate the account level xpub
|
||||
* m / purpose' / coin_type' / account'
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#path-levels
|
||||
* */
|
||||
case class AccountDb(xpub: ExtPublicKey, hdAccount: HDAccount) {
|
||||
def xpubVersion: ExtKeyPubVersion = xpub.version
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue