mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-21 14:14:28 +01:00
Implement ability to provide external entropy to bitcoin-s (#3672)
* Encapsulate initialization of DLCOracle.start() method * Use internal WalletAppConfig.kmConf rather than passing in custom key manager parameters * Add KeyManagerAppConfig.defaultAccountType * Get all tests passing besides TrezorAddressTest * Get TrezorAddressTest passing with provided entropy * Add unit test to make sure we can always derive the seed * Get docs compiling * Fix dlcWalletTest test cases * Add more test cases to keymanager * Add the new configuration to the example configuration * Add more test cases * Remove coverage on 2.12 as it isn't accurate * Rework DLCOracleAppConfig.start() to call kmConf.start() so the oracle can use entropy provided via bitcoin-s.conf
This commit is contained in:
parent
3c64af39d9
commit
132479d271
28 changed files with 465 additions and 362 deletions
|
@ -28,4 +28,4 @@ jobs:
|
|||
~/.bitcoin-s/binaries
|
||||
key: ${{ runner.os }}-cache
|
||||
- name: run tests
|
||||
run: sbt ++2.12.14 downloadBitcoind coverage keyManagerTest/test keyManager/coverageReport keyManager/coverageAggregate keyManager/coveralls feeProviderTest/test walletTest/test dlcWalletTest/test wallet/coverageReport wallet/coverageAggregate wallet/coveralls dlcOracleTest/test asyncUtilsTestJVM/test asyncUtilsTestJS/test oracleExplorerClient/test dlcOracle/coverageReport dlcOracle/coverageAggregate dlcOracle/coveralls
|
||||
run: sbt ++2.12.14 downloadBitcoind keyManagerTest/test feeProviderTest/test walletTest/test dlcWalletTest/test dlcOracleTest/test asyncUtilsTestJVM/test asyncUtilsTestJS/test oracleExplorerClient/test
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.oracle.server
|
|||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.commons.util.{DatadirParser, ServerArgParser}
|
||||
import org.bitcoins.dlc.oracle.DLCOracle
|
||||
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
||||
import org.bitcoins.server.routes.{BitcoinSServerRunner, Server}
|
||||
import org.bitcoins.server.util.BitcoinSAppScalaDaemon
|
||||
|
@ -22,8 +23,7 @@ class OracleServerMain(override val serverArgParser: ServerArgParser)(implicit
|
|||
|
||||
for {
|
||||
_ <- conf.start()
|
||||
oracle <- conf.initialize()
|
||||
|
||||
oracle = new DLCOracle()
|
||||
routes = Seq(OracleRoutes(oracle))
|
||||
server = serverArgParser.rpcPortOpt match {
|
||||
case Some(rpcport) =>
|
||||
|
|
|
@ -117,13 +117,11 @@ object BitcoindRpcBackendUtil extends Logging {
|
|||
val walletCallbackP = Promise[Wallet]()
|
||||
|
||||
val pairedWallet = Wallet(
|
||||
keyManager = wallet.keyManager,
|
||||
nodeApi =
|
||||
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
||||
walletCallbackP.future),
|
||||
chainQueryApi = bitcoind,
|
||||
feeRateApi = wallet.feeRateApi,
|
||||
creationTime = wallet.keyManager.creationTime
|
||||
feeRateApi = wallet.feeRateApi
|
||||
)(wallet.walletConfig, wallet.ec)
|
||||
|
||||
walletCallbackP.success(pairedWallet)
|
||||
|
@ -178,13 +176,11 @@ object BitcoindRpcBackendUtil extends Logging {
|
|||
val walletCallbackP = Promise[Wallet]()
|
||||
|
||||
val pairedWallet = DLCWallet(
|
||||
keyManager = wallet.keyManager,
|
||||
nodeApi =
|
||||
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
||||
walletCallbackP.future),
|
||||
chainQueryApi = bitcoind,
|
||||
feeRateApi = wallet.feeRateApi,
|
||||
creationTime = wallet.keyManager.creationTime
|
||||
feeRateApi = wallet.feeRateApi
|
||||
)(wallet.walletConfig, wallet.dlcConfig, wallet.ec)
|
||||
|
||||
walletCallbackP.success(pairedWallet)
|
||||
|
|
|
@ -220,4 +220,22 @@ class CryptoUtilTest extends BitcoinSCryptoTest {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
it must "do basic sanity checks on entropy" in {
|
||||
assert(!CryptoUtil.checkEntropy(BitVector.empty))
|
||||
val sameBytes1 = ByteVector.fill(32)(0x0)
|
||||
val sameBytes2 = ByteVector.fill(32)(0xff)
|
||||
assert(!CryptoUtil.checkEntropy(sameBytes1.toBitVector))
|
||||
assert(!CryptoUtil.checkEntropy(sameBytes2.toBitVector))
|
||||
|
||||
//to short of entropy
|
||||
val toShort = ByteVector.fromValidHex("0123456789abcdef")
|
||||
assert(!CryptoUtil.checkEntropy(toShort.toBitVector))
|
||||
}
|
||||
|
||||
it must "always pass our basic sanity tests for entropy with our real PRNG" in {
|
||||
forAll(Gen.const(CryptoUtil.randomBytes(32))) { bytes =>
|
||||
assert(CryptoUtil.checkEntropy(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -396,4 +396,25 @@ trait CryptoRuntime {
|
|||
derivedKeyLength: Int): ByteVector
|
||||
|
||||
def randomBytes(n: Int): ByteVector
|
||||
|
||||
/** Implements basic sanity tests for checking entropy like
|
||||
* making sure it isn't all the same bytes,
|
||||
* it isn't all 0x00...00
|
||||
* or it isn't all 0xffff...fff
|
||||
*/
|
||||
def checkEntropy(bitVector: BitVector): Boolean = {
|
||||
val byteArr = bitVector.toByteArray
|
||||
if (bitVector.length < 128) {
|
||||
//not enough entropy
|
||||
false
|
||||
} else if (byteArr.toSet.size == 1) {
|
||||
//means all byte were the same
|
||||
//we need more diversity with entropy
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def checkEntropy(bytes: ByteVector): Boolean = checkEntropy(bytes.toBitVector)
|
||||
}
|
||||
|
|
|
@ -109,7 +109,8 @@ class DLCOracleTest extends DLCOracleFixture {
|
|||
Vector(ConfigFactory.parseString("bitcoin-s.network = mainnet"),
|
||||
ConfigFactory.parseString("bitcoin-s.oracle.db.name = oracle1")))
|
||||
|
||||
newConf.initialize().flatMap { oracleB =>
|
||||
newConf.start().flatMap { _ =>
|
||||
val oracleB = new DLCOracle()(newConf)
|
||||
assert(oracleA.publicKey == oracleB.publicKey)
|
||||
|
||||
val eventName = "test"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.bitcoins.dlc.oracle.config
|
||||
|
||||
import org.bitcoins.dlc.oracle.DLCOracle
|
||||
import org.bitcoins.keymanager.WalletStorage
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.fixtures.DLCOracleAppConfigFixture
|
||||
|
@ -10,14 +11,15 @@ class DLCOracleAppConfigTest extends DLCOracleAppConfigFixture {
|
|||
|
||||
behavior of "DLCOracleAppConfig"
|
||||
|
||||
it must "initialize the same oracle twice" in {
|
||||
it must "start the same oracle twice" in {
|
||||
dlcOracleAppConfig: DLCOracleAppConfig =>
|
||||
val dlcOracle1F = dlcOracleAppConfig.initialize()
|
||||
val dlcOracle2F = dlcOracleAppConfig.initialize()
|
||||
|
||||
val started1F = dlcOracleAppConfig.start()
|
||||
val started2F = dlcOracleAppConfig.start()
|
||||
for {
|
||||
dlcOracle1 <- dlcOracle1F
|
||||
dlcOracle2 <- dlcOracle2F
|
||||
_ <- started1F
|
||||
_ <- started2F
|
||||
dlcOracle1 = new DLCOracle()(dlcOracleAppConfig)
|
||||
dlcOracle2 = new DLCOracle()(dlcOracleAppConfig)
|
||||
} yield {
|
||||
assert(dlcOracle1.publicKey == dlcOracle2.publicKey)
|
||||
}
|
||||
|
@ -26,12 +28,17 @@ class DLCOracleAppConfigTest extends DLCOracleAppConfigFixture {
|
|||
it must "initialize the oracle, move the seed somewhere else, and then start the oracle again and get the same pubkeys" in {
|
||||
dlcOracleAppConfig: DLCOracleAppConfig =>
|
||||
val seedFile = dlcOracleAppConfig.seedPath
|
||||
val dlcOracle1F = dlcOracleAppConfig.initialize()
|
||||
val pubKeyBeforeMoveF = dlcOracle1F.map(_.publicKey)
|
||||
val startedF = dlcOracleAppConfig.start()
|
||||
val pubKeyBeforeMoveF = for {
|
||||
_ <- startedF
|
||||
dlcOracle = new DLCOracle()(dlcOracleAppConfig)
|
||||
} yield {
|
||||
dlcOracle.publicKey
|
||||
}
|
||||
|
||||
//stop old oracle
|
||||
val stoppedF = for {
|
||||
_ <- dlcOracle1F
|
||||
_ <- startedF
|
||||
_ <- dlcOracleAppConfig.stop()
|
||||
} yield ()
|
||||
|
||||
|
@ -43,13 +50,21 @@ class DLCOracleAppConfigTest extends DLCOracleAppConfigFixture {
|
|||
|
||||
//create seed directory
|
||||
Files.createDirectories(newSeedPath.getParent)
|
||||
//copy seed file to new directory
|
||||
Files.copy(seedFile, newSeedPath)
|
||||
val copyF = startedF.map { _ =>
|
||||
//copy seed file to new directory
|
||||
Files.copy(seedFile, newSeedPath)
|
||||
}
|
||||
|
||||
//start the new app config from the new datadir
|
||||
val dlcOracle2F = DLCOracleAppConfig
|
||||
val appConfig = DLCOracleAppConfig
|
||||
.fromDatadir(newDatadir)
|
||||
.initialize()
|
||||
|
||||
val started2F = for {
|
||||
_ <- copyF
|
||||
_ <- appConfig.start()
|
||||
} yield ()
|
||||
|
||||
val dlcOracle2F = started2F.map(_ => new DLCOracle()(appConfig))
|
||||
|
||||
for {
|
||||
_ <- stoppedF
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.api.dlcoracle._
|
|||
import org.bitcoins.core.api.dlcoracle.db._
|
||||
import org.bitcoins.core.config.BitcoinNetwork
|
||||
import org.bitcoins.core.crypto.ExtKeyVersion.SegWitMainNetPriv
|
||||
import org.bitcoins.core.crypto.{ExtPrivateKeyHardened, MnemonicCode}
|
||||
import org.bitcoins.core.crypto.ExtPrivateKeyHardened
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.number._
|
||||
import org.bitcoins.core.protocol.Bech32Address
|
||||
|
@ -19,7 +19,7 @@ import org.bitcoins.crypto._
|
|||
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
||||
import org.bitcoins.dlc.oracle.storage._
|
||||
import org.bitcoins.dlc.oracle.util.EventDbUtil
|
||||
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
|
||||
import org.bitcoins.keymanager.WalletStorage
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.nio.file.Path
|
||||
|
@ -436,20 +436,6 @@ object DLCOracle {
|
|||
// 585 is a random one I picked, unclaimed in https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
val R_VALUE_PURPOSE = 585
|
||||
|
||||
def apply(mnemonicCode: MnemonicCode)(implicit
|
||||
conf: DLCOracleAppConfig): DLCOracle = {
|
||||
val decryptedMnemonic = DecryptedMnemonic(mnemonicCode, TimeUtil.now)
|
||||
val toWrite = conf.aesPasswordOpt match {
|
||||
case Some(password) => decryptedMnemonic.encrypt(password)
|
||||
case None => decryptedMnemonic
|
||||
}
|
||||
if (!conf.seedExists()) {
|
||||
WalletStorage.writeSeedToDisk(conf.kmConf.seedPath, toWrite)
|
||||
}
|
||||
|
||||
new DLCOracle()
|
||||
}
|
||||
|
||||
/** Gets the DLC oracle from the given datadir */
|
||||
def fromDatadir(path: Path, configs: Vector[Config])(implicit
|
||||
ec: ExecutionContext): Future[DLCOracle] = {
|
||||
|
|
|
@ -55,11 +55,13 @@ case class DLCOracleAppConfig(
|
|||
|
||||
override def start(): Future[Unit] = {
|
||||
logger.debug(s"Initializing dlc oracle setup")
|
||||
super.start().flatMap { _ =>
|
||||
val migrationsF = for {
|
||||
_ <- super.start()
|
||||
_ <- kmConf.start()
|
||||
} yield {
|
||||
if (Files.notExists(datadir)) {
|
||||
Files.createDirectories(datadir)
|
||||
}
|
||||
|
||||
val networkDir = {
|
||||
val lastDirname = network match {
|
||||
case MainNet => "mainnet"
|
||||
|
@ -69,7 +71,6 @@ case class DLCOracleAppConfig(
|
|||
}
|
||||
baseDatadir.resolve(lastDirname)
|
||||
}
|
||||
|
||||
// Move old db in network folder to oracle folder
|
||||
val oldNetworkLocation = networkDir.resolve("oracle.sqlite")
|
||||
if (!exists() && Files.exists(oldNetworkLocation)) {
|
||||
|
@ -80,32 +81,17 @@ case class DLCOracleAppConfig(
|
|||
logger.info(s"Applied $numMigrations to the dlc oracle project")
|
||||
|
||||
val migrations = migrationsApplied()
|
||||
val migrationWorkAroundF =
|
||||
if (migrations == 2 || migrations == 3) { // For V2/V3 migrations
|
||||
logger.debug(s"Doing V2/V3 Migration")
|
||||
migrations
|
||||
}
|
||||
|
||||
val dummyMigrationTLV = EnumEventDescriptorV0TLV.dummy
|
||||
migrationsF.flatMap { migrations =>
|
||||
val migrationWorkAroundF = v2V3MigrationWorkaround(migrations)
|
||||
|
||||
val eventDAO = EventDAO()(ec, appConfig)
|
||||
for {
|
||||
// get all old events
|
||||
allEvents <- eventDAO.findByEventDescriptor(dummyMigrationTLV)
|
||||
allOutcomes <- EventOutcomeDAO()(ec, appConfig).findAll()
|
||||
|
||||
outcomesByNonce = allOutcomes.groupBy(_.nonce)
|
||||
// Update them to have the correct event descriptor
|
||||
updated = allEvents.map { eventDb =>
|
||||
val outcomeDbs = outcomesByNonce(eventDb.nonce)
|
||||
val descriptor =
|
||||
EventOutcomeDbHelper.createEnumEventDescriptor(outcomeDbs)
|
||||
eventDb.copy(eventDescriptorTLV = descriptor)
|
||||
}
|
||||
|
||||
_ <- eventDAO.upsertAll(updated)
|
||||
} yield ()
|
||||
} else Future.unit
|
||||
|
||||
migrationWorkAroundF.map { _ =>
|
||||
val initializeF = initializeKeyManager()
|
||||
for {
|
||||
_ <- initializeF
|
||||
_ <- migrationWorkAroundF
|
||||
} yield {
|
||||
if (isHikariLoggingEnabled) {
|
||||
//.get is safe because hikari logging is enabled
|
||||
startHikariLogger(hikariLoggingInterval.get)
|
||||
|
@ -146,7 +132,7 @@ case class DLCOracleAppConfig(
|
|||
seedExists() && hasDb
|
||||
}
|
||||
|
||||
def initialize(): Future[DLCOracle] = {
|
||||
private def initializeKeyManager(): Future[DLCOracle] = {
|
||||
if (!seedExists()) {
|
||||
BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||
kmParams = kmParams,
|
||||
|
@ -157,7 +143,8 @@ case class DLCOracleAppConfig(
|
|||
}
|
||||
}
|
||||
|
||||
DLCOracle.fromDatadir(directory, confs.toVector)
|
||||
val o = new DLCOracle()(this)
|
||||
Future.successful(o)
|
||||
}
|
||||
|
||||
private lazy val rValueTable: TableQuery[Table[_]] = {
|
||||
|
@ -174,6 +161,38 @@ case class DLCOracleAppConfig(
|
|||
|
||||
override def allTables: List[TableQuery[Table[_]]] =
|
||||
List(rValueTable, eventTable, eventOutcomeTable)
|
||||
|
||||
/** @param migrations - The number of migrations we have run */
|
||||
private def v2V3MigrationWorkaround(migrations: Int): Future[Unit] = {
|
||||
val migrationWorkAroundF: Future[Unit] = {
|
||||
if (migrations == 2 || migrations == 3) { // For V2/V3 migrations
|
||||
logger.debug(s"Doing V2/V3 Migration")
|
||||
|
||||
val dummyMigrationTLV = EnumEventDescriptorV0TLV.dummy
|
||||
|
||||
val eventDAO = EventDAO()(ec, appConfig)
|
||||
for {
|
||||
// get all old events
|
||||
allEvents <- eventDAO.findByEventDescriptor(dummyMigrationTLV)
|
||||
allOutcomes <- EventOutcomeDAO()(ec, appConfig).findAll()
|
||||
|
||||
outcomesByNonce = allOutcomes.groupBy(_.nonce)
|
||||
// Update them to have the correct event descriptor
|
||||
updated = allEvents.map { eventDb =>
|
||||
val outcomeDbs = outcomesByNonce(eventDb.nonce)
|
||||
val descriptor =
|
||||
EventOutcomeDbHelper.createEnumEventDescriptor(outcomeDbs)
|
||||
eventDb.copy(eventDescriptorTLV = descriptor)
|
||||
}
|
||||
|
||||
_ <- eventDAO.upsertAll(updated)
|
||||
} yield ()
|
||||
} else {
|
||||
Future.unit
|
||||
}
|
||||
}
|
||||
migrationWorkAroundF
|
||||
}
|
||||
}
|
||||
|
||||
object DLCOracleAppConfig extends AppConfigFactory[DLCOracleAppConfig] {
|
||||
|
|
|
@ -6,10 +6,8 @@ import org.bitcoins.core.api.chain.ChainQueryApi
|
|||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerInitializeError
|
||||
import org.bitcoins.db.DatabaseDriver._
|
||||
import org.bitcoins.db._
|
||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||
|
||||
|
@ -114,42 +112,17 @@ object DLCAppConfig extends AppConfigFactory[DLCAppConfig] with WalletLogger {
|
|||
walletConf: WalletAppConfig,
|
||||
dlcConf: DLCAppConfig,
|
||||
ec: ExecutionContext): Future[DLCWallet] = {
|
||||
val aesPasswordOpt = walletConf.aesPasswordOpt
|
||||
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
||||
walletConf.hasWallet().flatMap { walletExists =>
|
||||
if (walletExists) {
|
||||
logger.info(s"Using pre-existing wallet")
|
||||
// TODO change me when we implement proper password handling
|
||||
BIP39LockedKeyManager.unlock(aesPasswordOpt,
|
||||
bip39PasswordOpt,
|
||||
walletConf.kmParams) match {
|
||||
case Right(km) =>
|
||||
val wallet =
|
||||
DLCWallet(km, nodeApi, chainQueryApi, feeRateApi, km.creationTime)
|
||||
Future.successful(wallet)
|
||||
case Left(err) =>
|
||||
sys.error(s"Error initializing key manager, err=${err}")
|
||||
}
|
||||
val wallet =
|
||||
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
||||
Future.successful(wallet)
|
||||
} else {
|
||||
logger.info(s"Initializing key manager")
|
||||
val keyManagerE: Either[KeyManagerInitializeError, BIP39KeyManager] =
|
||||
BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||
kmParams = walletConf.kmParams,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
|
||||
val keyManager = keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
sys.error(s"Error initializing key manager, err=${err}")
|
||||
}
|
||||
|
||||
logger.info(s"Creating new wallet")
|
||||
val unInitializedWallet =
|
||||
DLCWallet(keyManager,
|
||||
nodeApi,
|
||||
chainQueryApi,
|
||||
feeRateApi,
|
||||
keyManager.creationTime)
|
||||
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
||||
|
||||
Wallet
|
||||
.initialize(wallet = unInitializedWallet,
|
||||
|
|
|
@ -29,12 +29,10 @@ import org.bitcoins.crypto._
|
|||
import org.bitcoins.dlc.wallet.internal._
|
||||
import org.bitcoins.dlc.wallet.models._
|
||||
import org.bitcoins.dlc.wallet.util.DLCStatusBuilder
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.time.Instant
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
/** A [[Wallet]] with full DLC Functionality */
|
||||
|
@ -1504,11 +1502,9 @@ abstract class DLCWallet
|
|||
object DLCWallet extends WalletLogger {
|
||||
|
||||
private case class DLCWalletImpl(
|
||||
keyManager: BIP39KeyManager,
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
override val creationTime: Instant
|
||||
feeRateApi: FeeRateApi
|
||||
)(implicit
|
||||
val walletConfig: WalletAppConfig,
|
||||
val dlcConfig: DLCAppConfig,
|
||||
|
@ -1516,14 +1512,12 @@ object DLCWallet extends WalletLogger {
|
|||
) extends DLCWallet
|
||||
|
||||
def apply(
|
||||
keyManager: BIP39KeyManager,
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
creationTime: Instant)(implicit
|
||||
feeRateApi: FeeRateApi)(implicit
|
||||
config: WalletAppConfig,
|
||||
dlcConfig: DLCAppConfig,
|
||||
ec: ExecutionContext): DLCWallet = {
|
||||
DLCWalletImpl(keyManager, nodeApi, chainQueryApi, feeRateApi, creationTime)
|
||||
DLCWalletImpl(nodeApi, chainQueryApi, feeRateApi)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.bitcoins.core.protocol.blockchain.Block
|
|||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.wallet.fee._
|
||||
import org.bitcoins.feeprovider._
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node._
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config._
|
||||
|
@ -23,8 +22,6 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
|||
import org.bitcoins.wallet.Wallet
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||
```
|
||||
|
||||
|
@ -81,17 +78,6 @@ val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_
|
|||
val bitcoind = BitcoindV19RpcClient(instance)
|
||||
val nodeApi = BitcoinSWalletTest.MockNodeApi
|
||||
|
||||
// Create our key manager
|
||||
val keyManagerE = BIP39KeyManager.initialize(aesPasswordOpt = Some(AesPassword.fromString("password")),
|
||||
kmParams = walletConf.kmParams,
|
||||
bip39PasswordOpt = None)
|
||||
|
||||
val keyManager = keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
throw new RuntimeException(s"Cannot initialize key manager err=$err")
|
||||
}
|
||||
|
||||
// This function can be used to create a callback for when our chain api receives a transaction, block, or
|
||||
// a block filter, the returned NodeCallbacks will contain the necessary items to initialize the callbacks
|
||||
def createCallbacks(
|
||||
|
@ -198,7 +184,7 @@ val chainApi = new ChainQueryApi {
|
|||
|
||||
// Finally, we can initialize our wallet with our own node api
|
||||
val wallet =
|
||||
Wallet(keyManager = keyManager, nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one), creationTime = Instant.now)
|
||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Then to trigger one of the events we can run
|
||||
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
||||
|
|
|
@ -265,6 +265,11 @@ bitcoin-s {
|
|||
|
||||
# Password that your seed is encrypted with
|
||||
aesPassword = changeMe
|
||||
|
||||
# At least 16 bytes of entropy encoded in hex
|
||||
# This will be used as the seed for any
|
||||
# project that is dependent on the keymanager
|
||||
entropy = ""
|
||||
}
|
||||
|
||||
# Bitcoin-S provides manny different fee providers
|
||||
|
|
|
@ -11,7 +11,6 @@ import org.bitcoins.core.protocol.transaction.Transaction
|
|||
import org.bitcoins.core.wallet.fee._
|
||||
import org.bitcoins.core.util._
|
||||
import org.bitcoins.feeprovider._
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node._
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config._
|
||||
|
@ -20,7 +19,6 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
|||
import org.bitcoins.wallet.Wallet
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||
```
|
||||
|
@ -60,17 +58,6 @@ val bitcoind = BitcoindV19RpcClient(instance)
|
|||
val chainApi = BitcoinSWalletTest.MockChainQueryApi
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
// Create our key manager
|
||||
val keyManagerE = BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||
kmParams = walletConf.kmParams,
|
||||
bip39PasswordOpt = None)
|
||||
|
||||
val keyManager = keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
throw new RuntimeException(s"Cannot initialize key manager err=$err")
|
||||
}
|
||||
|
||||
// This function can be used to create a callback for when our node api calls downloadBlocks,
|
||||
// more specifically it will call the function every time we receive a block, the returned
|
||||
// NodeCallbacks will contain the necessary items to initialize the callbacks
|
||||
|
@ -105,7 +92,7 @@ val exampleCallback = createCallback(exampleProcessBlock)
|
|||
|
||||
// Finally, we can initialize our wallet with our own node api
|
||||
val wallet =
|
||||
Wallet(keyManager = keyManager, nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one), creationTime = Instant.now)
|
||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Then to trigger the event we can run
|
||||
val exampleBlock = DoubleSha256Digest(
|
||||
|
|
|
@ -27,13 +27,11 @@ import org.bitcoins.crypto._
|
|||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.wallet.fee._
|
||||
import org.bitcoins.feeprovider._
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.wallet._
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import java.time.Instant
|
||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||
```
|
||||
|
||||
|
@ -50,17 +48,6 @@ implicit val walletConf: WalletAppConfig =
|
|||
val bitcoind = BitcoindV19RpcClient(BitcoindInstanceLocal.fromConfFile())
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
// Create our key manager
|
||||
val keyManagerE = BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||
kmParams = walletConf.kmParams,
|
||||
bip39PasswordOpt = None)
|
||||
|
||||
val keyManager = keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
throw new RuntimeException(s"Cannot initialize key manager err=$err")
|
||||
}
|
||||
|
||||
// Here is a super simple example of a callback, this could be replaced with anything, from
|
||||
// relaying the transaction on the network, finding relevant wallet outputs, verifying the transaction,
|
||||
// or writing it to disk
|
||||
|
@ -73,11 +60,10 @@ val exampleCallbacks = WalletCallbacks(
|
|||
|
||||
// Now we can create a wallet
|
||||
val wallet =
|
||||
Wallet(keyManager = keyManager,
|
||||
Wallet(
|
||||
nodeApi = bitcoind,
|
||||
chainQueryApi = bitcoind,
|
||||
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one),
|
||||
creationTime = Instant.now)
|
||||
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Finally, we can add the callbacks to our wallet config
|
||||
walletConf.addCallbacks(exampleCallbacks)
|
||||
|
|
|
@ -115,12 +115,9 @@ val getBlockFunc = {hash: DoubleSha256DigestBE => bitcoind.getBlockRaw(hash) }
|
|||
//yay! We are now all setup. Using our 3 functions above and a wallet, we can now sync
|
||||
//a fresh wallet
|
||||
implicit val walletAppConfig = WalletAppConfig.fromDefaultDatadir()
|
||||
implicit val kmAppConfig = KeyManagerAppConfig.fromDefaultDatadir()
|
||||
val keyManager: BIP39KeyManager = {
|
||||
BIP39KeyManager.fromParams(walletAppConfig.kmParams,None,None).right.get
|
||||
}
|
||||
|
||||
val feeRateProvider: FeeRateApi = MempoolSpaceProvider.fromBlockTarget(6, proxyParams = None)
|
||||
val wallet = Wallet(keyManager, bitcoind, bitcoind, feeRateProvider, keyManager.creationTime)
|
||||
val wallet = Wallet(bitcoind, bitcoind, feeRateProvider)
|
||||
|
||||
//yay! we have a synced wallet
|
||||
val syncedWalletF = WalletSync.syncFullBlocks(wallet,
|
||||
|
|
|
@ -143,23 +143,14 @@ val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
|||
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
||||
}
|
||||
|
||||
//initialize our key manager, where we store our keys
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
//you can add a password here if you want
|
||||
//val bip39PasswordOpt = Some("my-password-here")
|
||||
val bip39PasswordOpt = None
|
||||
val keyManager = BIP39KeyManager.initialize(aesPasswordOpt, walletConfig.kmParams, bip39PasswordOpt).getOrElse {
|
||||
throw new RuntimeException(s"Failed to initalize key manager")
|
||||
}
|
||||
|
||||
// once this future completes, we have a initialized
|
||||
// wallet
|
||||
val wallet = Wallet(keyManager, new NodeApi {
|
||||
val wallet = Wallet(new NodeApi {
|
||||
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
||||
override def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = Future.successful(())
|
||||
}, chainApi, ConstantFeeRateProvider(SatoshisPerVirtualByte.one), creationTime = Instant.now)
|
||||
}, chainApi, ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
||||
Wallet.initialize(wallet,bip39PasswordOpt)
|
||||
Wallet.initialize(wallet, None)
|
||||
}
|
||||
|
||||
// when this future completes, ww have sent a transaction
|
||||
|
|
|
@ -20,7 +20,7 @@ class WalletStorageTest extends BitcoinSWalletTest with BeforeAndAfterEach {
|
|||
override type FixtureParam = WalletAppConfig
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withWalletConfig(test)
|
||||
withWalletConfigNotStarted(test)
|
||||
|
||||
def getSeedPath(config: WalletAppConfig): Path = {
|
||||
config.kmConf.seedPath
|
||||
|
|
|
@ -2,12 +2,14 @@ package org.bitcoins.keymanager.config
|
|||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
|
||||
import org.bitcoins.core.crypto.{BIP39Seed, ExtKeyVersion, MnemonicCode}
|
||||
import org.bitcoins.core.util.TimeUtil
|
||||
import org.bitcoins.crypto.CryptoUtil
|
||||
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
||||
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
|
||||
|
@ -107,4 +109,108 @@ class KeyManagerAppConfigTest extends BitcoinSAsyncTest {
|
|||
.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)))
|
||||
}
|
||||
}
|
||||
|
||||
it must "initialize the keymanager with external entropy" in {
|
||||
val tmpDir2 = BitcoinSTestAppConfig.tmpDir()
|
||||
val tempFile = Files.createFile(tmpDir2.resolve("bitcoin-s.conf"))
|
||||
val entropy = CryptoUtil.randomBytes(16)
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
| keymanager.entropy=${entropy.toHex}
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig1 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
val appConfig2 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
val started1F = appConfig1.start()
|
||||
val started2F = appConfig2.start()
|
||||
for {
|
||||
_ <- started1F
|
||||
_ <- started2F
|
||||
} yield {
|
||||
//make sure they are internally consistent
|
||||
assert(
|
||||
appConfig1.toBip39KeyManager.getRootXPub == appConfig2.toBip39KeyManager.getRootXPub)
|
||||
|
||||
//manually build the xpub to make sure we are correct
|
||||
val mnemonic = MnemonicCode.fromEntropy(entropy)
|
||||
val bip39Seed = BIP39Seed.fromMnemonic(mnemonic, None)
|
||||
val xpriv = bip39Seed.toExtPrivateKey(ExtKeyVersion.LegacyTestNet3Priv)
|
||||
val xpub = xpriv.extPublicKey
|
||||
assert(xpub == appConfig1.toBip39KeyManager.getRootXPub)
|
||||
}
|
||||
}
|
||||
|
||||
it must "initialize correctly with entropy set in the config file" in {
|
||||
val tmpDir2 = BitcoinSTestAppConfig.tmpDir()
|
||||
val tempFile = Files.createFile(tmpDir2.resolve("bitcoin-s.conf"))
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig1 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
appConfig1
|
||||
.start()
|
||||
.map(_ => succeed)
|
||||
}
|
||||
|
||||
it must "fail to start the key manager when there isn't enough entropy" in {
|
||||
val tmpDir2 = BitcoinSTestAppConfig.tmpDir()
|
||||
val tempFile = Files.createFile(tmpDir2.resolve("bitcoin-s.conf"))
|
||||
val entropy = CryptoUtil.randomBytes(15)
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
| keymanager.entropy=${entropy.toHex}
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig1 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
|
||||
assertThrows[RuntimeException] {
|
||||
appConfig1.start()
|
||||
}
|
||||
}
|
||||
|
||||
it must "fail to start the key manager when the entropy isn't hex chars" in {
|
||||
val tmpDir2 = BitcoinSTestAppConfig.tmpDir()
|
||||
val tempFile = Files.createFile(tmpDir2.resolve("bitcoin-s.conf"))
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
| keymanager.entropy=invalidhexcharactersfortestcase
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig1 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
|
||||
assertThrows[RuntimeException] {
|
||||
appConfig1.start()
|
||||
}
|
||||
}
|
||||
|
||||
it must "fail to get bip39KeyManager when we haven't called start" in {
|
||||
val tmpDir2 = BitcoinSTestAppConfig.tmpDir()
|
||||
val tempFile = Files.createFile(tmpDir2.resolve("bitcoin-s.conf"))
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
| keymanager.entropy=invalidhexcharactersfortestcase
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig1 = KeyManagerAppConfig(directory = tmpDir2)
|
||||
|
||||
assertThrows[RuntimeException] {
|
||||
appConfig1.toBip39KeyManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ object BIP39KeyManager
|
|||
|
||||
val time = TimeUtil.now
|
||||
|
||||
val writtenToDiskE: Either[KeyManagerInitializeError, KeyManagerApi] =
|
||||
val writtenToDiskE: Either[KeyManagerInitializeError, KeyManagerApi] = {
|
||||
if (Files.notExists(seedPath)) {
|
||||
logger.info(
|
||||
s"Seed path parent directory does not exist, creating ${seedPath.getParent}")
|
||||
|
@ -159,6 +159,7 @@ object BIP39KeyManager
|
|||
JsonParsingError(err.toString)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//verify we can unlock it for a sanity check
|
||||
val unlocked = BIP39LockedKeyManager.unlock(passphraseOpt = aesPasswordOpt,
|
||||
|
|
|
@ -3,8 +3,13 @@ package org.bitcoins.keymanager.config
|
|||
import com.typesafe.config.Config
|
||||
import org.bitcoins.commons.config.{AppConfig, AppConfigFactory, ConfigOps}
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.crypto.AesPassword
|
||||
import org.bitcoins.keymanager.WalletStorage
|
||||
import org.bitcoins.core.crypto.MnemonicCode
|
||||
import org.bitcoins.core.hd.{HDPurpose, HDPurposes}
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||
import org.bitcoins.crypto.{AesPassword, CryptoUtil}
|
||||
import org.bitcoins.keymanager.{ReadMnemonicError, WalletStorage}
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import scodec.bits.BitVector
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -46,6 +51,29 @@ case class KeyManagerAppConfig(
|
|||
seedFolder.resolve(s"$prefix${WalletStorage.ENCRYPTED_SEED_FILE_NAME}")
|
||||
}
|
||||
|
||||
private lazy val defaultAccountKind: HDPurpose =
|
||||
config.getString("bitcoin-s.wallet.defaultAccountType") match {
|
||||
case "legacy" => HDPurposes.Legacy
|
||||
case "segwit" => HDPurposes.SegWit
|
||||
case "nested-segwit" => HDPurposes.NestedSegWit
|
||||
// todo: validate this pre-app startup
|
||||
case other: String =>
|
||||
throw new RuntimeException(s"$other is not a valid account type!")
|
||||
}
|
||||
|
||||
/** Entropy provided by the a user in their bitcoin-s.conf
|
||||
* configuration file. This should be used to seed the keymanager
|
||||
* rather than randomly generating entropy.
|
||||
*/
|
||||
private lazy val externalEntropy: Option[String] = {
|
||||
val opt = config.getStringOrNone("bitcoin-s.keymanager.entropy")
|
||||
opt
|
||||
}
|
||||
|
||||
private val kmParams: KeyManagerParams = {
|
||||
KeyManagerParams(seedPath, defaultAccountKind, network)
|
||||
}
|
||||
|
||||
override def start(): Future[Unit] = {
|
||||
val oldDefaultFile =
|
||||
baseDatadir.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
|
||||
|
@ -59,8 +87,16 @@ case class KeyManagerAppConfig(
|
|||
// Create directory
|
||||
Files.createDirectories(newDefaultFile.getParent)
|
||||
Files.copy(oldDefaultFile, newDefaultFile)
|
||||
logger.info(
|
||||
s"Migrated keymanager seed from=${oldDefaultFile.toAbsolutePath} to=${newDefaultFile.toAbsolutePath}")
|
||||
Future.unit
|
||||
} else if (!Files.exists(newDefaultFile)) {
|
||||
initializeKeyManager()
|
||||
} else {
|
||||
logger.info(
|
||||
s"Starting keymanager with seedPath=${seedPath.toAbsolutePath}")
|
||||
Future.unit
|
||||
}
|
||||
Future.unit
|
||||
}
|
||||
|
||||
override def stop(): Future[Unit] = Future.unit
|
||||
|
@ -78,6 +114,66 @@ case class KeyManagerAppConfig(
|
|||
def seedExists(): Boolean = {
|
||||
WalletStorage.seedExists(seedPath)
|
||||
}
|
||||
|
||||
/** Creates a [[BIP39KeyManager]] from the seed referenced by this [[KeyManagerAppConfig]]
|
||||
* with the given wallet purpose
|
||||
*/
|
||||
def toBip39KeyManager: BIP39KeyManager = {
|
||||
val kmE: Either[ReadMnemonicError, BIP39KeyManager] =
|
||||
BIP39KeyManager.fromParams(kmParams = kmParams,
|
||||
passwordOpt = aesPasswordOpt,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
kmE match {
|
||||
case Left(err) =>
|
||||
sys.error(
|
||||
s"Could not create a BIP39KeyManager from the KeyManagerAppConfig, err=$err")
|
||||
case Right(km) =>
|
||||
km
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes the key manager. Takes into consideration if external entropy
|
||||
* has been provided to bitcoin-s via the bitcoin-s.conf file
|
||||
*/
|
||||
private def initializeKeyManager(): Future[Unit] = {
|
||||
val entropy: BitVector = externalEntropy match {
|
||||
case Some(entropy) =>
|
||||
logger.info(
|
||||
s"Initializing new mnemonic seed at path=${seedPath.toAbsolutePath} with external entropy")
|
||||
val hexOpt = BitVector.fromHex(entropy)
|
||||
hexOpt match {
|
||||
case Some(hex) => hex
|
||||
case None =>
|
||||
sys.error(
|
||||
s"Entropy provided by bitcoin-s.keymanager.entropy was not valid hex, got=${entropy}")
|
||||
}
|
||||
case None =>
|
||||
logger.info(
|
||||
s"Initializing new mnemonic seed at path=${seedPath.toAbsolutePath}")
|
||||
MnemonicCode.getEntropy256Bits
|
||||
}
|
||||
|
||||
if (!CryptoUtil.checkEntropy(entropy)) {
|
||||
sys.error(
|
||||
s"The entropy used by bitcoin-s does not pass basic entropy sanity checks, got=$entropy")
|
||||
}
|
||||
|
||||
val initE = BIP39KeyManager.initializeWithEntropy(
|
||||
aesPasswordOpt = aesPasswordOpt,
|
||||
entropy = entropy,
|
||||
bip39PasswordOpt = bip39PasswordOpt,
|
||||
kmParams = kmParams)
|
||||
initE match {
|
||||
case Right(km) =>
|
||||
logger.info(
|
||||
s"Successfully initialize seed at path with root xpub=${km.getRootXPub}")
|
||||
Future.unit
|
||||
case Left(err) =>
|
||||
Future.failed(
|
||||
new RuntimeException(
|
||||
s"Failed to initialize mnemonic seed in keymanager with err=$err"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object KeyManagerAppConfig extends AppConfigFactory[KeyManagerAppConfig] {
|
||||
|
|
|
@ -114,7 +114,7 @@ sealed abstract class CryptoGenerators {
|
|||
* @see https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#From_mnemonic_to_seed
|
||||
*/
|
||||
def bip39Password: Gen[String] = {
|
||||
Gen.asciiStr
|
||||
Gen.alphaNumStr
|
||||
}
|
||||
|
||||
/** Generates a valid BIP39 seed from
|
||||
|
|
|
@ -18,8 +18,9 @@ trait DLCOracleFixture extends BitcoinSFixture with EmbeddedPg {
|
|||
BitcoinSTestAppConfig.getDLCOracleWithEmbeddedDbTestConfig(pgUrl)
|
||||
val _ = conf.migrate()
|
||||
|
||||
val oracleF: Future[DLCOracle] = conf.initialize()
|
||||
oracleF
|
||||
val oracleConfF: Future[Unit] = conf.start()
|
||||
|
||||
oracleConfF.map(_ => new DLCOracle()(conf))
|
||||
}
|
||||
|
||||
val destroy: DLCOracle => Future[Unit] = dlcOracle => {
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.bitcoins.core.util.FutureUtil
|
|||
import org.bitcoins.core.wallet.fee._
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.node.{
|
||||
NodeCallbacks,
|
||||
OnBlockReceived,
|
||||
|
@ -73,11 +72,9 @@ trait BitcoinSWalletTest
|
|||
walletConfig =>
|
||||
implicit val newWalletConf =
|
||||
getFreshWalletAppConfig.withOverrides(walletConfig)
|
||||
val km = createNewKeyManager()(newWalletConf)
|
||||
val bip39PasswordOpt = KeyManagerTestUtil.bip39PasswordOpt
|
||||
makeDependentFixture(
|
||||
build = createNewWallet(keyManager = km,
|
||||
bip39PasswordOpt = bip39PasswordOpt,
|
||||
build = createNewWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||
extraConfig = Some(walletConfig),
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi),
|
||||
|
@ -239,6 +236,17 @@ trait BitcoinSWalletTest
|
|||
}
|
||||
makeDependentFixture(builder, destroy = destroy)(test)
|
||||
}
|
||||
|
||||
def withWalletConfigNotStarted(test: OneArgAsyncTest): FutureOutcome = {
|
||||
val builder: () => Future[WalletAppConfig] = () => {
|
||||
createWalletAppConfigNotStarted(pgUrl, Vector.empty)
|
||||
}
|
||||
|
||||
val destroy: WalletAppConfig => Future[Unit] = walletAppConfig => {
|
||||
destroyWalletAppConfig(walletAppConfig)
|
||||
}
|
||||
makeDependentFixture(builder, destroy = destroy)(test)
|
||||
}
|
||||
}
|
||||
|
||||
object BitcoinSWalletTest extends WalletLogger {
|
||||
|
@ -293,20 +301,6 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
Future.successful(0)
|
||||
}
|
||||
|
||||
private def createNewKeyManager(
|
||||
bip39PasswordOpt: Option[String] = KeyManagerTestUtil.bip39PasswordOpt)(
|
||||
implicit config: WalletAppConfig): BIP39KeyManager = {
|
||||
val keyManagerE = BIP39KeyManager.initialize(config.aesPasswordOpt,
|
||||
kmParams = config.kmParams,
|
||||
bip39PasswordOpt =
|
||||
bip39PasswordOpt)
|
||||
keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
throw new RuntimeException(s"Cannot initialize key manager err=${err}")
|
||||
}
|
||||
}
|
||||
|
||||
private[bitcoins] class RandomFeeProvider extends FeeRateApi {
|
||||
// Useful for tests
|
||||
var lastFeeRate: Option[FeeUnit] = None
|
||||
|
@ -324,6 +318,17 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
configs: Vector[Config])(implicit
|
||||
system: ActorSystem): Future[WalletAppConfig] = {
|
||||
import system.dispatcher
|
||||
val walletAppConfigF = createWalletAppConfigNotStarted(pgUrl, configs)
|
||||
for {
|
||||
appConfig <- walletAppConfigF
|
||||
_ <- appConfig.start()
|
||||
} yield appConfig
|
||||
}
|
||||
|
||||
def createWalletAppConfigNotStarted(
|
||||
pgUrl: () => Option[String],
|
||||
configs: Vector[Config])(implicit
|
||||
system: ActorSystem): Future[WalletAppConfig] = {
|
||||
val baseConf = BaseWalletTest.getFreshWalletAppConfig(pgUrl, configs)
|
||||
val walletNameOpt = if (NumberGenerator.bool.sampleSome) {
|
||||
Some(StringGenerators.genNonEmptyString.sampleSome)
|
||||
|
@ -341,7 +346,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
case None => baseConf
|
||||
}
|
||||
|
||||
walletConf.start().map(_ => walletConf)
|
||||
Future.successful(walletConf)
|
||||
}
|
||||
|
||||
/** Returns a function that can be used to create a wallet fixture.
|
||||
|
@ -351,66 +356,81 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
* or account type.
|
||||
*/
|
||||
private def createNewWallet(
|
||||
keyManager: BIP39KeyManager,
|
||||
bip39PasswordOpt: Option[String],
|
||||
extraConfig: Option[Config],
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi)(implicit
|
||||
config: WalletAppConfig,
|
||||
ec: ExecutionContext): () => Future[Wallet] =
|
||||
() => {
|
||||
ec: ExecutionContext): () => Future[Wallet] = { () =>
|
||||
{
|
||||
val walletConfig = extraConfig match {
|
||||
case None => config
|
||||
case Some(c) => config.withOverrides(c)
|
||||
}
|
||||
|
||||
val walletConfigWithBip39Pw = bip39PasswordOpt match {
|
||||
case Some(pw) =>
|
||||
val str = s"""bitcoin-s.keymanager.bip39password="$pw""""
|
||||
val bip39Config = ConfigFactory.parseString(str)
|
||||
walletConfig.withOverrides(bip39Config)
|
||||
case None => walletConfig
|
||||
}
|
||||
|
||||
// we want to check we're not overwriting
|
||||
// any user data
|
||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
||||
|
||||
walletConfig.start().flatMap { _ =>
|
||||
walletConfigWithBip39Pw.start().flatMap { _ =>
|
||||
val wallet =
|
||||
Wallet(keyManager,
|
||||
nodeApi,
|
||||
chainQueryApi,
|
||||
new RandomFeeProvider,
|
||||
keyManager.creationTime)(walletConfig, ec)
|
||||
Wallet.initialize(wallet, bip39PasswordOpt)
|
||||
Wallet(nodeApi, chainQueryApi, new RandomFeeProvider)(
|
||||
walletConfigWithBip39Pw,
|
||||
ec)
|
||||
Wallet.initialize(wallet, bip39PasswordOpt)(walletConfigWithBip39Pw, ec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def createDLCWallet(
|
||||
keyManager: BIP39KeyManager,
|
||||
bip39PasswordOpt: Option[String],
|
||||
extraConfig: Option[Config],
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi)(implicit
|
||||
config: BitcoinSAppConfig,
|
||||
ec: ExecutionContext): Future[DLCWallet] = {
|
||||
val defaultConf = config.walletConf
|
||||
|
||||
val walletConfig = extraConfig match {
|
||||
case None => defaultConf
|
||||
case Some(c) => defaultConf.withOverrides(c)
|
||||
case None => config
|
||||
case Some(c) => config.withOverrides(c)
|
||||
}
|
||||
|
||||
val walletConfigWithBip39Pw = bip39PasswordOpt match {
|
||||
case Some(pw) =>
|
||||
val str = s"""bitcoin-s.keymanager.bip39password="$pw""""
|
||||
val bip39Config = ConfigFactory.parseString(str)
|
||||
walletConfig.withOverrides(bip39Config)
|
||||
case None => walletConfig
|
||||
}
|
||||
|
||||
// we want to check we're not overwriting
|
||||
// any user data
|
||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
||||
AppConfig.throwIfDefaultDatadir(walletConfigWithBip39Pw.walletConf)
|
||||
|
||||
val initConfs = for {
|
||||
_ <- walletConfig.start()
|
||||
_ <- walletConfigWithBip39Pw.walletConf.start()
|
||||
_ <- config.dlcConf.start()
|
||||
} yield ()
|
||||
|
||||
initConfs.flatMap { _ =>
|
||||
val wallet =
|
||||
DLCWallet(keyManager,
|
||||
nodeApi,
|
||||
chainQueryApi,
|
||||
new RandomFeeProvider,
|
||||
keyManager.creationTime)(walletConfig, config.dlcConf, ec)
|
||||
DLCWallet(nodeApi, chainQueryApi, new RandomFeeProvider)(
|
||||
walletConfigWithBip39Pw.walletConf,
|
||||
config.dlcConf,
|
||||
ec)
|
||||
|
||||
Wallet
|
||||
.initialize(wallet, bip39PasswordOpt)(walletConfig, ec)
|
||||
.initialize(wallet, bip39PasswordOpt)(
|
||||
walletConfigWithBip39Pw.walletConf,
|
||||
ec)
|
||||
.map(_.asInstanceOf[DLCWallet])
|
||||
}
|
||||
}
|
||||
|
@ -429,14 +449,12 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
case Some(walletConf) =>
|
||||
config.withOverrides(walletConf)
|
||||
}
|
||||
val km =
|
||||
createNewKeyManager(bip39PasswordOpt = bip39PasswordOpt)(newWalletConf)
|
||||
createNewWallet(
|
||||
keyManager = km,
|
||||
bip39PasswordOpt = bip39PasswordOpt,
|
||||
extraConfig = extraConfig,
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi)(config, ec)() // get the standard config
|
||||
createNewWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||
extraConfig = extraConfig,
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi)(newWalletConf,
|
||||
ec
|
||||
)() // get the standard config
|
||||
}
|
||||
|
||||
/** Creates a default wallet with bitcoind where the [[ChainQueryApi]] fed to the wallet
|
||||
|
@ -462,12 +480,10 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
//create the wallet with the appropriate callbacks now that
|
||||
//we have them
|
||||
walletWithCallback = Wallet(
|
||||
keyManager = wallet.keyManager,
|
||||
nodeApi =
|
||||
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
|
||||
chainQueryApi = bitcoind,
|
||||
feeRateApi = new RandomFeeProvider,
|
||||
creationTime = wallet.keyManager.creationTime
|
||||
feeRateApi = new RandomFeeProvider
|
||||
)(wallet.walletConfig, wallet.ec)
|
||||
//complete the walletCallbackP so we can handle the callbacks when they are
|
||||
//called without hanging forever.
|
||||
|
@ -508,15 +524,11 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||
config: BitcoinSAppConfig,
|
||||
system: ActorSystem): Future[DLCWallet] = {
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
val km =
|
||||
createNewKeyManager(bip39PasswordOpt = bip39PasswordOpt)(
|
||||
config.walletConf)
|
||||
for {
|
||||
wallet <- createDLCWallet(km,
|
||||
bip39PasswordOpt,
|
||||
extraConfig,
|
||||
nodeApi,
|
||||
chainQueryApi)
|
||||
wallet <- createDLCWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||
extraConfig = extraConfig,
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi)
|
||||
account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
||||
newAccountWallet <- wallet.createNewAccount(hdAccount = account1,
|
||||
kmParams =
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
import org.bitcoins.core.api.wallet.db._
|
||||
import org.bitcoins.core.crypto.{ExtPublicKey, MnemonicCode}
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.util.{FutureUtil, TimeUtil}
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||
import org.bitcoins.feeprovider.ConstantFeeRateProvider
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.testkit.fixtures.EmptyFixture
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
|
@ -26,6 +24,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
import scala.io.Source
|
||||
|
||||
class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||
|
||||
val mnemonic = MnemonicCode.fromWords(
|
||||
Vector(
|
||||
|
@ -131,42 +130,35 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
|||
lazy val nestedVectors =
|
||||
vectors.filter(_.pathType == HDPurposes.NestedSegWit)
|
||||
|
||||
def configForPurpose(purpose: HDPurpose): Config = {
|
||||
def configForPurposeAndSeed(purpose: HDPurpose): Config = {
|
||||
val purposeStr = purpose match {
|
||||
case HDPurposes.Legacy => "legacy"
|
||||
case HDPurposes.SegWit => "segwit"
|
||||
case HDPurposes.NestedSegWit => "nested-segwit"
|
||||
case other => fail(s"unexpected purpose: $other")
|
||||
}
|
||||
val entropy = mnemonic.toEntropy.toHex
|
||||
val confStr = s"""bitcoin-s.wallet.defaultAccountType = $purposeStr
|
||||
|bitcoin-s.network = mainnet""".stripMargin
|
||||
|bitcoin-s.network = mainnet
|
||||
|bitcoin-s.keymanager.entropy=${entropy}
|
||||
|""".stripMargin
|
||||
ConfigFactory.parseString(confStr)
|
||||
}
|
||||
|
||||
private def getWallet(config: WalletAppConfig)(implicit
|
||||
ec: ExecutionContext): Future[Wallet] = {
|
||||
import system.dispatcher
|
||||
val bip39PasswordOpt = None
|
||||
val kmE = BIP39KeyManager.initializeWithEntropy(
|
||||
aesPasswordOpt = config.aesPasswordOpt,
|
||||
entropy = mnemonic.toEntropy,
|
||||
bip39PasswordOpt = bip39PasswordOpt,
|
||||
kmParams = config.kmParams)
|
||||
kmE match {
|
||||
case Left(err) =>
|
||||
Future.failed(
|
||||
new RuntimeException(s"Failed to initialize km with err=${err}"))
|
||||
case Right(km) =>
|
||||
val wallet =
|
||||
Wallet(km,
|
||||
MockNodeApi,
|
||||
MockChainQueryApi,
|
||||
ConstantFeeRateProvider(SatoshisPerVirtualByte.one),
|
||||
TimeUtil.now)(config, ec)
|
||||
val walletF =
|
||||
Wallet.initialize(wallet = wallet,
|
||||
bip39PasswordOpt = bip39PasswordOpt)(config, ec)
|
||||
walletF
|
||||
}
|
||||
val startedF = config.start()
|
||||
for {
|
||||
_ <- startedF
|
||||
wallet =
|
||||
Wallet(MockNodeApi,
|
||||
MockChainQueryApi,
|
||||
ConstantFeeRateProvider(SatoshisPerVirtualByte.one))(config, ec)
|
||||
init <- Wallet.initialize(wallet = wallet,
|
||||
bip39PasswordOpt = bip39PasswordOpt)(config, ec)
|
||||
} yield init
|
||||
}
|
||||
|
||||
case class AccountAndAddrsAndVector(
|
||||
|
@ -230,7 +222,7 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
|||
}
|
||||
|
||||
private def testAccountType(purpose: HDPurpose): Future[Assertion] = {
|
||||
val confOverride = configForPurpose(purpose)
|
||||
val confOverride = configForPurposeAndSeed(purpose)
|
||||
implicit val conf: WalletAppConfig =
|
||||
BitcoinSTestAppConfig.getSpvTestConfig(confOverride).walletConf
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@ import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType}
|
|||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script._
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerUnlockError
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerUnlockError.MnemonicNotFound
|
||||
import org.bitcoins.crypto.{AesPassword, DoubleSha256DigestBE, ECPublicKey}
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPublicKey}
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.testkitcore.util.TransactionTestUtil._
|
||||
import org.scalatest.FutureOutcome
|
||||
|
@ -126,21 +124,6 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
} yield res
|
||||
}
|
||||
|
||||
it should "fail to unlock the wallet with a bad aes password" in {
|
||||
wallet: Wallet =>
|
||||
val badPassphrase = Some(AesPassword.fromNonEmptyString("bad"))
|
||||
|
||||
val errorType = wallet.unlock(badPassphrase, None) match {
|
||||
case Right(_) => fail("Unlocked wallet with bad password!")
|
||||
case Left(err) => err
|
||||
}
|
||||
errorType match {
|
||||
case KeyManagerUnlockError.MnemonicNotFound => fail(MnemonicNotFound)
|
||||
case KeyManagerUnlockError.BadPassword => succeed
|
||||
case KeyManagerUnlockError.JsonParsingError(message) => fail(message)
|
||||
}
|
||||
}
|
||||
|
||||
it should "match block filters" in { wallet: Wallet =>
|
||||
for {
|
||||
height <- wallet.chainQueryApi.getFilterCount()
|
||||
|
|
|
@ -27,14 +27,11 @@ import org.bitcoins.core.script.control.OP_RETURN
|
|||
import org.bitcoins.core.util.{BitcoinScriptUtil, FutureUtil, HDUtil}
|
||||
import org.bitcoins.core.wallet.builder._
|
||||
import org.bitcoins.core.wallet.fee._
|
||||
import org.bitcoins.core.wallet.keymanagement.{
|
||||
KeyManagerParams,
|
||||
KeyManagerUnlockError
|
||||
}
|
||||
import org.bitcoins.core.wallet.keymanagement.{KeyManagerParams}
|
||||
import org.bitcoins.core.wallet.utxo.TxoState._
|
||||
import org.bitcoins.core.wallet.utxo._
|
||||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
|
||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager}
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.internal._
|
||||
import org.bitcoins.wallet.models._
|
||||
|
@ -54,7 +51,9 @@ abstract class Wallet
|
|||
with RescanHandling
|
||||
with WalletLogger {
|
||||
|
||||
override def keyManager: BIP39KeyManager
|
||||
override def keyManager: BIP39KeyManager = {
|
||||
walletConfig.kmConf.toBip39KeyManager
|
||||
}
|
||||
|
||||
implicit val ec: ExecutionContext
|
||||
|
||||
|
@ -230,29 +229,6 @@ abstract class Wallet
|
|||
}
|
||||
}
|
||||
|
||||
def unlock(
|
||||
passphraseOpt: Option[AesPassword],
|
||||
bip39PasswordOpt: Option[String]): Either[
|
||||
KeyManagerUnlockError,
|
||||
Wallet] = {
|
||||
val kmParams = walletConfig.kmParams
|
||||
|
||||
val unlockedKeyManagerE =
|
||||
BIP39LockedKeyManager.unlock(passphraseOpt = passphraseOpt,
|
||||
bip39PasswordOpt = bip39PasswordOpt,
|
||||
kmParams = kmParams)
|
||||
unlockedKeyManagerE match {
|
||||
case Right(km) =>
|
||||
val w = Wallet(keyManager = km,
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi,
|
||||
feeRateApi = feeRateApi,
|
||||
creationTime = km.creationTime)
|
||||
Right(w)
|
||||
case Left(err) => Left(err)
|
||||
}
|
||||
}
|
||||
|
||||
override def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
||||
for {
|
||||
_ <- nodeApi.broadcastTransaction(transaction)
|
||||
|
@ -962,25 +938,21 @@ abstract class Wallet
|
|||
object Wallet extends WalletLogger {
|
||||
|
||||
private case class WalletImpl(
|
||||
keyManager: BIP39KeyManager,
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
override val creationTime: Instant
|
||||
feeRateApi: FeeRateApi
|
||||
)(implicit
|
||||
val walletConfig: WalletAppConfig,
|
||||
val ec: ExecutionContext
|
||||
) extends Wallet
|
||||
|
||||
def apply(
|
||||
keyManager: BIP39KeyManager,
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
creationTime: Instant)(implicit
|
||||
feeRateApi: FeeRateApi)(implicit
|
||||
config: WalletAppConfig,
|
||||
ec: ExecutionContext): Wallet = {
|
||||
WalletImpl(keyManager, nodeApi, chainQueryApi, feeRateApi, creationTime)
|
||||
WalletImpl(nodeApi, chainQueryApi, feeRateApi)
|
||||
}
|
||||
|
||||
/** Creates the level 0 account for the given HD purpose, if the root account exists do nothing */
|
||||
|
|
|
@ -8,14 +8,10 @@ import org.bitcoins.core.api.feeprovider.FeeRateApi
|
|||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.util.Mutable
|
||||
import org.bitcoins.core.wallet.keymanagement.{
|
||||
KeyManagerInitializeError,
|
||||
KeyManagerParams
|
||||
}
|
||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||
import org.bitcoins.crypto.AesPassword
|
||||
import org.bitcoins.db.DatabaseDriver.{PostgreSQL, SQLite}
|
||||
import org.bitcoins.db._
|
||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
|
||||
import org.bitcoins.keymanager.config.KeyManagerAppConfig
|
||||
import org.bitcoins.tor.config.TorAppConfig
|
||||
import org.bitcoins.wallet.config.WalletAppConfig.RebroadcastTransactionsRunnable
|
||||
|
@ -24,14 +20,7 @@ import org.bitcoins.wallet.models.AccountDAO
|
|||
import org.bitcoins.wallet.{Wallet, WalletCallbacks, WalletLogger}
|
||||
|
||||
import java.nio.file.{Files, Path, Paths}
|
||||
import java.util.concurrent.{
|
||||
ExecutorService,
|
||||
Executors,
|
||||
ScheduledExecutorService,
|
||||
ScheduledFuture,
|
||||
ThreadFactory,
|
||||
TimeUnit
|
||||
}
|
||||
import java.util.concurrent._
|
||||
import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}
|
||||
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||
|
||||
|
@ -182,6 +171,7 @@ case class WalletAppConfig(
|
|||
override def start(): Future[Unit] = {
|
||||
for {
|
||||
_ <- super.start()
|
||||
_ <- kmConf.start()
|
||||
} yield {
|
||||
logger.debug(s"Initializing wallet setup")
|
||||
|
||||
|
@ -343,42 +333,17 @@ object WalletAppConfig
|
|||
walletConf: WalletAppConfig,
|
||||
ec: ExecutionContext): Future[Wallet] = {
|
||||
walletConf.hasWallet().flatMap { walletExists =>
|
||||
val aesPasswordOpt = walletConf.aesPasswordOpt
|
||||
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
||||
|
||||
if (walletExists) {
|
||||
logger.info(s"Using pre-existing wallet")
|
||||
// TODO change me when we implement proper password handling
|
||||
BIP39LockedKeyManager.unlock(aesPasswordOpt,
|
||||
bip39PasswordOpt,
|
||||
walletConf.kmParams) match {
|
||||
case Right(km) =>
|
||||
val wallet =
|
||||
Wallet(km, nodeApi, chainQueryApi, feeRateApi, km.creationTime)
|
||||
Future.successful(wallet)
|
||||
case Left(err) =>
|
||||
sys.error(s"Error initializing key manager, err=${err}")
|
||||
}
|
||||
val wallet =
|
||||
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
||||
Future.successful(wallet)
|
||||
} else {
|
||||
logger.info(s"Initializing key manager")
|
||||
val keyManagerE: Either[KeyManagerInitializeError, BIP39KeyManager] =
|
||||
BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||
kmParams = walletConf.kmParams,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
|
||||
val keyManager = keyManagerE match {
|
||||
case Right(keyManager) => keyManager
|
||||
case Left(err) =>
|
||||
sys.error(s"Error initializing key manager, err=${err}")
|
||||
}
|
||||
|
||||
logger.info(s"Creating new wallet")
|
||||
val unInitializedWallet =
|
||||
Wallet(keyManager,
|
||||
nodeApi,
|
||||
chainQueryApi,
|
||||
feeRateApi,
|
||||
keyManager.creationTime)
|
||||
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
||||
|
||||
Wallet.initialize(wallet = unInitializedWallet,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
|
|
Loading…
Add table
Reference in a new issue