mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 06:31:55 +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
|
~/.bitcoin-s/binaries
|
||||||
key: ${{ runner.os }}-cache
|
key: ${{ runner.os }}-cache
|
||||||
- name: run tests
|
- 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 akka.actor.ActorSystem
|
||||||
import org.bitcoins.commons.util.{DatadirParser, ServerArgParser}
|
import org.bitcoins.commons.util.{DatadirParser, ServerArgParser}
|
||||||
|
import org.bitcoins.dlc.oracle.DLCOracle
|
||||||
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
import org.bitcoins.dlc.oracle.config.DLCOracleAppConfig
|
||||||
import org.bitcoins.server.routes.{BitcoinSServerRunner, Server}
|
import org.bitcoins.server.routes.{BitcoinSServerRunner, Server}
|
||||||
import org.bitcoins.server.util.BitcoinSAppScalaDaemon
|
import org.bitcoins.server.util.BitcoinSAppScalaDaemon
|
||||||
|
@ -22,8 +23,7 @@ class OracleServerMain(override val serverArgParser: ServerArgParser)(implicit
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_ <- conf.start()
|
_ <- conf.start()
|
||||||
oracle <- conf.initialize()
|
oracle = new DLCOracle()
|
||||||
|
|
||||||
routes = Seq(OracleRoutes(oracle))
|
routes = Seq(OracleRoutes(oracle))
|
||||||
server = serverArgParser.rpcPortOpt match {
|
server = serverArgParser.rpcPortOpt match {
|
||||||
case Some(rpcport) =>
|
case Some(rpcport) =>
|
||||||
|
|
|
@ -117,13 +117,11 @@ object BitcoindRpcBackendUtil extends Logging {
|
||||||
val walletCallbackP = Promise[Wallet]()
|
val walletCallbackP = Promise[Wallet]()
|
||||||
|
|
||||||
val pairedWallet = Wallet(
|
val pairedWallet = Wallet(
|
||||||
keyManager = wallet.keyManager,
|
|
||||||
nodeApi =
|
nodeApi =
|
||||||
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
||||||
walletCallbackP.future),
|
walletCallbackP.future),
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind,
|
||||||
feeRateApi = wallet.feeRateApi,
|
feeRateApi = wallet.feeRateApi
|
||||||
creationTime = wallet.keyManager.creationTime
|
|
||||||
)(wallet.walletConfig, wallet.ec)
|
)(wallet.walletConfig, wallet.ec)
|
||||||
|
|
||||||
walletCallbackP.success(pairedWallet)
|
walletCallbackP.success(pairedWallet)
|
||||||
|
@ -178,13 +176,11 @@ object BitcoindRpcBackendUtil extends Logging {
|
||||||
val walletCallbackP = Promise[Wallet]()
|
val walletCallbackP = Promise[Wallet]()
|
||||||
|
|
||||||
val pairedWallet = DLCWallet(
|
val pairedWallet = DLCWallet(
|
||||||
keyManager = wallet.keyManager,
|
|
||||||
nodeApi =
|
nodeApi =
|
||||||
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
BitcoindRpcBackendUtil.getNodeApiWalletCallback(bitcoind,
|
||||||
walletCallbackP.future),
|
walletCallbackP.future),
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind,
|
||||||
feeRateApi = wallet.feeRateApi,
|
feeRateApi = wallet.feeRateApi
|
||||||
creationTime = wallet.keyManager.creationTime
|
|
||||||
)(wallet.walletConfig, wallet.dlcConfig, wallet.ec)
|
)(wallet.walletConfig, wallet.dlcConfig, wallet.ec)
|
||||||
|
|
||||||
walletCallbackP.success(pairedWallet)
|
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
|
derivedKeyLength: Int): ByteVector
|
||||||
|
|
||||||
def randomBytes(n: 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"),
|
Vector(ConfigFactory.parseString("bitcoin-s.network = mainnet"),
|
||||||
ConfigFactory.parseString("bitcoin-s.oracle.db.name = oracle1")))
|
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)
|
assert(oracleA.publicKey == oracleB.publicKey)
|
||||||
|
|
||||||
val eventName = "test"
|
val eventName = "test"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.bitcoins.dlc.oracle.config
|
package org.bitcoins.dlc.oracle.config
|
||||||
|
|
||||||
|
import org.bitcoins.dlc.oracle.DLCOracle
|
||||||
import org.bitcoins.keymanager.WalletStorage
|
import org.bitcoins.keymanager.WalletStorage
|
||||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
import org.bitcoins.testkit.fixtures.DLCOracleAppConfigFixture
|
import org.bitcoins.testkit.fixtures.DLCOracleAppConfigFixture
|
||||||
|
@ -10,14 +11,15 @@ class DLCOracleAppConfigTest extends DLCOracleAppConfigFixture {
|
||||||
|
|
||||||
behavior of "DLCOracleAppConfig"
|
behavior of "DLCOracleAppConfig"
|
||||||
|
|
||||||
it must "initialize the same oracle twice" in {
|
it must "start the same oracle twice" in {
|
||||||
dlcOracleAppConfig: DLCOracleAppConfig =>
|
dlcOracleAppConfig: DLCOracleAppConfig =>
|
||||||
val dlcOracle1F = dlcOracleAppConfig.initialize()
|
val started1F = dlcOracleAppConfig.start()
|
||||||
val dlcOracle2F = dlcOracleAppConfig.initialize()
|
val started2F = dlcOracleAppConfig.start()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
dlcOracle1 <- dlcOracle1F
|
_ <- started1F
|
||||||
dlcOracle2 <- dlcOracle2F
|
_ <- started2F
|
||||||
|
dlcOracle1 = new DLCOracle()(dlcOracleAppConfig)
|
||||||
|
dlcOracle2 = new DLCOracle()(dlcOracleAppConfig)
|
||||||
} yield {
|
} yield {
|
||||||
assert(dlcOracle1.publicKey == dlcOracle2.publicKey)
|
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 {
|
it must "initialize the oracle, move the seed somewhere else, and then start the oracle again and get the same pubkeys" in {
|
||||||
dlcOracleAppConfig: DLCOracleAppConfig =>
|
dlcOracleAppConfig: DLCOracleAppConfig =>
|
||||||
val seedFile = dlcOracleAppConfig.seedPath
|
val seedFile = dlcOracleAppConfig.seedPath
|
||||||
val dlcOracle1F = dlcOracleAppConfig.initialize()
|
val startedF = dlcOracleAppConfig.start()
|
||||||
val pubKeyBeforeMoveF = dlcOracle1F.map(_.publicKey)
|
val pubKeyBeforeMoveF = for {
|
||||||
|
_ <- startedF
|
||||||
|
dlcOracle = new DLCOracle()(dlcOracleAppConfig)
|
||||||
|
} yield {
|
||||||
|
dlcOracle.publicKey
|
||||||
|
}
|
||||||
|
|
||||||
//stop old oracle
|
//stop old oracle
|
||||||
val stoppedF = for {
|
val stoppedF = for {
|
||||||
_ <- dlcOracle1F
|
_ <- startedF
|
||||||
_ <- dlcOracleAppConfig.stop()
|
_ <- dlcOracleAppConfig.stop()
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
|
@ -43,13 +50,21 @@ class DLCOracleAppConfigTest extends DLCOracleAppConfigFixture {
|
||||||
|
|
||||||
//create seed directory
|
//create seed directory
|
||||||
Files.createDirectories(newSeedPath.getParent)
|
Files.createDirectories(newSeedPath.getParent)
|
||||||
//copy seed file to new directory
|
val copyF = startedF.map { _ =>
|
||||||
Files.copy(seedFile, newSeedPath)
|
//copy seed file to new directory
|
||||||
|
Files.copy(seedFile, newSeedPath)
|
||||||
|
}
|
||||||
|
|
||||||
//start the new app config from the new datadir
|
//start the new app config from the new datadir
|
||||||
val dlcOracle2F = DLCOracleAppConfig
|
val appConfig = DLCOracleAppConfig
|
||||||
.fromDatadir(newDatadir)
|
.fromDatadir(newDatadir)
|
||||||
.initialize()
|
|
||||||
|
val started2F = for {
|
||||||
|
_ <- copyF
|
||||||
|
_ <- appConfig.start()
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
val dlcOracle2F = started2F.map(_ => new DLCOracle()(appConfig))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_ <- stoppedF
|
_ <- stoppedF
|
||||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.api.dlcoracle._
|
||||||
import org.bitcoins.core.api.dlcoracle.db._
|
import org.bitcoins.core.api.dlcoracle.db._
|
||||||
import org.bitcoins.core.config.BitcoinNetwork
|
import org.bitcoins.core.config.BitcoinNetwork
|
||||||
import org.bitcoins.core.crypto.ExtKeyVersion.SegWitMainNetPriv
|
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.hd._
|
||||||
import org.bitcoins.core.number._
|
import org.bitcoins.core.number._
|
||||||
import org.bitcoins.core.protocol.Bech32Address
|
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.config.DLCOracleAppConfig
|
||||||
import org.bitcoins.dlc.oracle.storage._
|
import org.bitcoins.dlc.oracle.storage._
|
||||||
import org.bitcoins.dlc.oracle.util.EventDbUtil
|
import org.bitcoins.dlc.oracle.util.EventDbUtil
|
||||||
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
|
import org.bitcoins.keymanager.WalletStorage
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
import java.nio.file.Path
|
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
|
// 585 is a random one I picked, unclaimed in https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||||
val R_VALUE_PURPOSE = 585
|
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 */
|
/** Gets the DLC oracle from the given datadir */
|
||||||
def fromDatadir(path: Path, configs: Vector[Config])(implicit
|
def fromDatadir(path: Path, configs: Vector[Config])(implicit
|
||||||
ec: ExecutionContext): Future[DLCOracle] = {
|
ec: ExecutionContext): Future[DLCOracle] = {
|
||||||
|
|
|
@ -55,11 +55,13 @@ case class DLCOracleAppConfig(
|
||||||
|
|
||||||
override def start(): Future[Unit] = {
|
override def start(): Future[Unit] = {
|
||||||
logger.debug(s"Initializing dlc oracle setup")
|
logger.debug(s"Initializing dlc oracle setup")
|
||||||
super.start().flatMap { _ =>
|
val migrationsF = for {
|
||||||
|
_ <- super.start()
|
||||||
|
_ <- kmConf.start()
|
||||||
|
} yield {
|
||||||
if (Files.notExists(datadir)) {
|
if (Files.notExists(datadir)) {
|
||||||
Files.createDirectories(datadir)
|
Files.createDirectories(datadir)
|
||||||
}
|
}
|
||||||
|
|
||||||
val networkDir = {
|
val networkDir = {
|
||||||
val lastDirname = network match {
|
val lastDirname = network match {
|
||||||
case MainNet => "mainnet"
|
case MainNet => "mainnet"
|
||||||
|
@ -69,7 +71,6 @@ case class DLCOracleAppConfig(
|
||||||
}
|
}
|
||||||
baseDatadir.resolve(lastDirname)
|
baseDatadir.resolve(lastDirname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move old db in network folder to oracle folder
|
// Move old db in network folder to oracle folder
|
||||||
val oldNetworkLocation = networkDir.resolve("oracle.sqlite")
|
val oldNetworkLocation = networkDir.resolve("oracle.sqlite")
|
||||||
if (!exists() && Files.exists(oldNetworkLocation)) {
|
if (!exists() && Files.exists(oldNetworkLocation)) {
|
||||||
|
@ -80,32 +81,17 @@ case class DLCOracleAppConfig(
|
||||||
logger.info(s"Applied $numMigrations to the dlc oracle project")
|
logger.info(s"Applied $numMigrations to the dlc oracle project")
|
||||||
|
|
||||||
val migrations = migrationsApplied()
|
val migrations = migrationsApplied()
|
||||||
val migrationWorkAroundF =
|
migrations
|
||||||
if (migrations == 2 || migrations == 3) { // For V2/V3 migrations
|
}
|
||||||
logger.debug(s"Doing V2/V3 Migration")
|
|
||||||
|
|
||||||
val dummyMigrationTLV = EnumEventDescriptorV0TLV.dummy
|
migrationsF.flatMap { migrations =>
|
||||||
|
val migrationWorkAroundF = v2V3MigrationWorkaround(migrations)
|
||||||
|
|
||||||
val eventDAO = EventDAO()(ec, appConfig)
|
val initializeF = initializeKeyManager()
|
||||||
for {
|
for {
|
||||||
// get all old events
|
_ <- initializeF
|
||||||
allEvents <- eventDAO.findByEventDescriptor(dummyMigrationTLV)
|
_ <- migrationWorkAroundF
|
||||||
allOutcomes <- EventOutcomeDAO()(ec, appConfig).findAll()
|
} yield {
|
||||||
|
|
||||||
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 { _ =>
|
|
||||||
if (isHikariLoggingEnabled) {
|
if (isHikariLoggingEnabled) {
|
||||||
//.get is safe because hikari logging is enabled
|
//.get is safe because hikari logging is enabled
|
||||||
startHikariLogger(hikariLoggingInterval.get)
|
startHikariLogger(hikariLoggingInterval.get)
|
||||||
|
@ -146,7 +132,7 @@ case class DLCOracleAppConfig(
|
||||||
seedExists() && hasDb
|
seedExists() && hasDb
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize(): Future[DLCOracle] = {
|
private def initializeKeyManager(): Future[DLCOracle] = {
|
||||||
if (!seedExists()) {
|
if (!seedExists()) {
|
||||||
BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
BIP39KeyManager.initialize(aesPasswordOpt = aesPasswordOpt,
|
||||||
kmParams = kmParams,
|
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[_]] = {
|
private lazy val rValueTable: TableQuery[Table[_]] = {
|
||||||
|
@ -174,6 +161,38 @@ case class DLCOracleAppConfig(
|
||||||
|
|
||||||
override def allTables: List[TableQuery[Table[_]]] =
|
override def allTables: List[TableQuery[Table[_]]] =
|
||||||
List(rValueTable, eventTable, eventOutcomeTable)
|
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] {
|
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.feeprovider.FeeRateApi
|
||||||
import org.bitcoins.core.api.node.NodeApi
|
import org.bitcoins.core.api.node.NodeApi
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerInitializeError
|
|
||||||
import org.bitcoins.db.DatabaseDriver._
|
import org.bitcoins.db.DatabaseDriver._
|
||||||
import org.bitcoins.db._
|
import org.bitcoins.db._
|
||||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
|
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||||
|
|
||||||
|
@ -114,42 +112,17 @@ object DLCAppConfig extends AppConfigFactory[DLCAppConfig] with WalletLogger {
|
||||||
walletConf: WalletAppConfig,
|
walletConf: WalletAppConfig,
|
||||||
dlcConf: DLCAppConfig,
|
dlcConf: DLCAppConfig,
|
||||||
ec: ExecutionContext): Future[DLCWallet] = {
|
ec: ExecutionContext): Future[DLCWallet] = {
|
||||||
val aesPasswordOpt = walletConf.aesPasswordOpt
|
|
||||||
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
||||||
walletConf.hasWallet().flatMap { walletExists =>
|
walletConf.hasWallet().flatMap { walletExists =>
|
||||||
if (walletExists) {
|
if (walletExists) {
|
||||||
logger.info(s"Using pre-existing wallet")
|
logger.info(s"Using pre-existing wallet")
|
||||||
// TODO change me when we implement proper password handling
|
val wallet =
|
||||||
BIP39LockedKeyManager.unlock(aesPasswordOpt,
|
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
||||||
bip39PasswordOpt,
|
Future.successful(wallet)
|
||||||
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}")
|
|
||||||
}
|
|
||||||
} else {
|
} 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")
|
logger.info(s"Creating new wallet")
|
||||||
val unInitializedWallet =
|
val unInitializedWallet =
|
||||||
DLCWallet(keyManager,
|
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
||||||
nodeApi,
|
|
||||||
chainQueryApi,
|
|
||||||
feeRateApi,
|
|
||||||
keyManager.creationTime)
|
|
||||||
|
|
||||||
Wallet
|
Wallet
|
||||||
.initialize(wallet = unInitializedWallet,
|
.initialize(wallet = unInitializedWallet,
|
||||||
|
|
|
@ -29,12 +29,10 @@ import org.bitcoins.crypto._
|
||||||
import org.bitcoins.dlc.wallet.internal._
|
import org.bitcoins.dlc.wallet.internal._
|
||||||
import org.bitcoins.dlc.wallet.models._
|
import org.bitcoins.dlc.wallet.models._
|
||||||
import org.bitcoins.dlc.wallet.util.DLCStatusBuilder
|
import org.bitcoins.dlc.wallet.util.DLCStatusBuilder
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
|
||||||
import java.time.Instant
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
/** A [[Wallet]] with full DLC Functionality */
|
/** A [[Wallet]] with full DLC Functionality */
|
||||||
|
@ -1504,11 +1502,9 @@ abstract class DLCWallet
|
||||||
object DLCWallet extends WalletLogger {
|
object DLCWallet extends WalletLogger {
|
||||||
|
|
||||||
private case class DLCWalletImpl(
|
private case class DLCWalletImpl(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi
|
||||||
override val creationTime: Instant
|
|
||||||
)(implicit
|
)(implicit
|
||||||
val walletConfig: WalletAppConfig,
|
val walletConfig: WalletAppConfig,
|
||||||
val dlcConfig: DLCAppConfig,
|
val dlcConfig: DLCAppConfig,
|
||||||
|
@ -1516,14 +1512,12 @@ object DLCWallet extends WalletLogger {
|
||||||
) extends DLCWallet
|
) extends DLCWallet
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi)(implicit
|
||||||
creationTime: Instant)(implicit
|
|
||||||
config: WalletAppConfig,
|
config: WalletAppConfig,
|
||||||
dlcConfig: DLCAppConfig,
|
dlcConfig: DLCAppConfig,
|
||||||
ec: ExecutionContext): DLCWallet = {
|
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.protocol.transaction.Transaction
|
||||||
import org.bitcoins.core.wallet.fee._
|
import org.bitcoins.core.wallet.fee._
|
||||||
import org.bitcoins.feeprovider._
|
import org.bitcoins.feeprovider._
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.node._
|
import org.bitcoins.node._
|
||||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||||
import org.bitcoins.rpc.config._
|
import org.bitcoins.rpc.config._
|
||||||
|
@ -23,8 +22,6 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.wallet.Wallet
|
import org.bitcoins.wallet.Wallet
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -81,17 +78,6 @@ val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_
|
||||||
val bitcoind = BitcoindV19RpcClient(instance)
|
val bitcoind = BitcoindV19RpcClient(instance)
|
||||||
val nodeApi = BitcoinSWalletTest.MockNodeApi
|
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
|
// 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
|
// a block filter, the returned NodeCallbacks will contain the necessary items to initialize the callbacks
|
||||||
def createCallbacks(
|
def createCallbacks(
|
||||||
|
@ -198,7 +184,7 @@ val chainApi = new ChainQueryApi {
|
||||||
|
|
||||||
// Finally, we can initialize our wallet with our own node api
|
// Finally, we can initialize our wallet with our own node api
|
||||||
val wallet =
|
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
|
// Then to trigger one of the events we can run
|
||||||
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
||||||
|
|
|
@ -265,6 +265,11 @@ bitcoin-s {
|
||||||
|
|
||||||
# Password that your seed is encrypted with
|
# Password that your seed is encrypted with
|
||||||
aesPassword = changeMe
|
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
|
# 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.wallet.fee._
|
||||||
import org.bitcoins.core.util._
|
import org.bitcoins.core.util._
|
||||||
import org.bitcoins.feeprovider._
|
import org.bitcoins.feeprovider._
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.node._
|
import org.bitcoins.node._
|
||||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||||
import org.bitcoins.rpc.config._
|
import org.bitcoins.rpc.config._
|
||||||
|
@ -20,7 +19,6 @@ import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.wallet.Wallet
|
import org.bitcoins.wallet.Wallet
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
import java.time.Instant
|
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||||
```
|
```
|
||||||
|
@ -60,17 +58,6 @@ val bitcoind = BitcoindV19RpcClient(instance)
|
||||||
val chainApi = BitcoinSWalletTest.MockChainQueryApi
|
val chainApi = BitcoinSWalletTest.MockChainQueryApi
|
||||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
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,
|
// 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
|
// 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
|
// 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
|
// Finally, we can initialize our wallet with our own node api
|
||||||
val wallet =
|
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
|
// Then to trigger the event we can run
|
||||||
val exampleBlock = DoubleSha256Digest(
|
val exampleBlock = DoubleSha256Digest(
|
||||||
|
|
|
@ -27,13 +27,11 @@ import org.bitcoins.crypto._
|
||||||
import org.bitcoins.core.protocol.transaction.Transaction
|
import org.bitcoins.core.protocol.transaction.Transaction
|
||||||
import org.bitcoins.core.wallet.fee._
|
import org.bitcoins.core.wallet.fee._
|
||||||
import org.bitcoins.feeprovider._
|
import org.bitcoins.feeprovider._
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
import org.bitcoins.rpc.client.v19.BitcoindV19RpcClient
|
||||||
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
import org.bitcoins.rpc.config.BitcoindInstanceLocal
|
||||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
import org.bitcoins.wallet._
|
import org.bitcoins.wallet._
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
import java.time.Instant
|
|
||||||
import scala.concurrent.{ExecutionContextExecutor, Future}
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -50,17 +48,6 @@ implicit val walletConf: WalletAppConfig =
|
||||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstanceLocal.fromConfFile())
|
val bitcoind = BitcoindV19RpcClient(BitcoindInstanceLocal.fromConfFile())
|
||||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
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
|
// 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,
|
// relaying the transaction on the network, finding relevant wallet outputs, verifying the transaction,
|
||||||
// or writing it to disk
|
// or writing it to disk
|
||||||
|
@ -73,11 +60,10 @@ val exampleCallbacks = WalletCallbacks(
|
||||||
|
|
||||||
// Now we can create a wallet
|
// Now we can create a wallet
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(keyManager = keyManager,
|
Wallet(
|
||||||
nodeApi = bitcoind,
|
nodeApi = bitcoind,
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind,
|
||||||
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one),
|
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||||
creationTime = Instant.now)
|
|
||||||
|
|
||||||
// Finally, we can add the callbacks to our wallet config
|
// Finally, we can add the callbacks to our wallet config
|
||||||
walletConf.addCallbacks(exampleCallbacks)
|
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
|
//yay! We are now all setup. Using our 3 functions above and a wallet, we can now sync
|
||||||
//a fresh wallet
|
//a fresh wallet
|
||||||
implicit val walletAppConfig = WalletAppConfig.fromDefaultDatadir()
|
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 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
|
//yay! we have a synced wallet
|
||||||
val syncedWalletF = WalletSync.syncFullBlocks(wallet,
|
val syncedWalletF = WalletSync.syncFullBlocks(wallet,
|
||||||
|
|
|
@ -143,23 +143,14 @@ val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
||||||
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
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
|
// once this future completes, we have a initialized
|
||||||
// wallet
|
// wallet
|
||||||
val wallet = Wallet(keyManager, new NodeApi {
|
val wallet = Wallet(new NodeApi {
|
||||||
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
||||||
override def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): 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 { _ =>
|
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
||||||
Wallet.initialize(wallet,bip39PasswordOpt)
|
Wallet.initialize(wallet, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// when this future completes, ww have sent a transaction
|
// when this future completes, ww have sent a transaction
|
||||||
|
|
|
@ -20,7 +20,7 @@ class WalletStorageTest extends BitcoinSWalletTest with BeforeAndAfterEach {
|
||||||
override type FixtureParam = WalletAppConfig
|
override type FixtureParam = WalletAppConfig
|
||||||
|
|
||||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
withWalletConfig(test)
|
withWalletConfigNotStarted(test)
|
||||||
|
|
||||||
def getSeedPath(config: WalletAppConfig): Path = {
|
def getSeedPath(config: WalletAppConfig): Path = {
|
||||||
config.kmConf.seedPath
|
config.kmConf.seedPath
|
||||||
|
|
|
@ -2,12 +2,14 @@ package org.bitcoins.keymanager.config
|
||||||
|
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
|
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.core.util.TimeUtil
|
||||||
|
import org.bitcoins.crypto.CryptoUtil
|
||||||
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
|
import org.bitcoins.keymanager.{DecryptedMnemonic, WalletStorage}
|
||||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
||||||
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
import org.bitcoins.testkitcore.Implicits.GeneratorOps
|
||||||
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
import org.bitcoins.testkitcore.gen.CryptoGenerators
|
||||||
import org.bitcoins.testkit.util.BitcoinSAsyncTest
|
|
||||||
|
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
|
|
||||||
|
@ -107,4 +109,108 @@ class KeyManagerAppConfigTest extends BitcoinSAsyncTest {
|
||||||
.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)))
|
.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 time = TimeUtil.now
|
||||||
|
|
||||||
val writtenToDiskE: Either[KeyManagerInitializeError, KeyManagerApi] =
|
val writtenToDiskE: Either[KeyManagerInitializeError, KeyManagerApi] = {
|
||||||
if (Files.notExists(seedPath)) {
|
if (Files.notExists(seedPath)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
s"Seed path parent directory does not exist, creating ${seedPath.getParent}")
|
s"Seed path parent directory does not exist, creating ${seedPath.getParent}")
|
||||||
|
@ -159,6 +159,7 @@ object BIP39KeyManager
|
||||||
JsonParsingError(err.toString)))
|
JsonParsingError(err.toString)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//verify we can unlock it for a sanity check
|
//verify we can unlock it for a sanity check
|
||||||
val unlocked = BIP39LockedKeyManager.unlock(passphraseOpt = aesPasswordOpt,
|
val unlocked = BIP39LockedKeyManager.unlock(passphraseOpt = aesPasswordOpt,
|
||||||
|
|
|
@ -3,8 +3,13 @@ package org.bitcoins.keymanager.config
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import org.bitcoins.commons.config.{AppConfig, AppConfigFactory, ConfigOps}
|
import org.bitcoins.commons.config.{AppConfig, AppConfigFactory, ConfigOps}
|
||||||
import org.bitcoins.core.config.NetworkParameters
|
import org.bitcoins.core.config.NetworkParameters
|
||||||
import org.bitcoins.crypto.AesPassword
|
import org.bitcoins.core.crypto.MnemonicCode
|
||||||
import org.bitcoins.keymanager.WalletStorage
|
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 java.nio.file.{Files, Path}
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
@ -46,6 +51,29 @@ case class KeyManagerAppConfig(
|
||||||
seedFolder.resolve(s"$prefix${WalletStorage.ENCRYPTED_SEED_FILE_NAME}")
|
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] = {
|
override def start(): Future[Unit] = {
|
||||||
val oldDefaultFile =
|
val oldDefaultFile =
|
||||||
baseDatadir.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
|
baseDatadir.resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
|
||||||
|
@ -59,8 +87,16 @@ case class KeyManagerAppConfig(
|
||||||
// Create directory
|
// Create directory
|
||||||
Files.createDirectories(newDefaultFile.getParent)
|
Files.createDirectories(newDefaultFile.getParent)
|
||||||
Files.copy(oldDefaultFile, newDefaultFile)
|
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
|
override def stop(): Future[Unit] = Future.unit
|
||||||
|
@ -78,6 +114,66 @@ case class KeyManagerAppConfig(
|
||||||
def seedExists(): Boolean = {
|
def seedExists(): Boolean = {
|
||||||
WalletStorage.seedExists(seedPath)
|
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] {
|
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
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#From_mnemonic_to_seed
|
||||||
*/
|
*/
|
||||||
def bip39Password: Gen[String] = {
|
def bip39Password: Gen[String] = {
|
||||||
Gen.asciiStr
|
Gen.alphaNumStr
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates a valid BIP39 seed from
|
/** Generates a valid BIP39 seed from
|
||||||
|
|
|
@ -18,8 +18,9 @@ trait DLCOracleFixture extends BitcoinSFixture with EmbeddedPg {
|
||||||
BitcoinSTestAppConfig.getDLCOracleWithEmbeddedDbTestConfig(pgUrl)
|
BitcoinSTestAppConfig.getDLCOracleWithEmbeddedDbTestConfig(pgUrl)
|
||||||
val _ = conf.migrate()
|
val _ = conf.migrate()
|
||||||
|
|
||||||
val oracleF: Future[DLCOracle] = conf.initialize()
|
val oracleConfF: Future[Unit] = conf.start()
|
||||||
oracleF
|
|
||||||
|
oracleConfF.map(_ => new DLCOracle()(conf))
|
||||||
}
|
}
|
||||||
|
|
||||||
val destroy: DLCOracle => Future[Unit] = dlcOracle => {
|
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.core.wallet.fee._
|
||||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||||
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
|
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.node.{
|
import org.bitcoins.node.{
|
||||||
NodeCallbacks,
|
NodeCallbacks,
|
||||||
OnBlockReceived,
|
OnBlockReceived,
|
||||||
|
@ -73,11 +72,9 @@ trait BitcoinSWalletTest
|
||||||
walletConfig =>
|
walletConfig =>
|
||||||
implicit val newWalletConf =
|
implicit val newWalletConf =
|
||||||
getFreshWalletAppConfig.withOverrides(walletConfig)
|
getFreshWalletAppConfig.withOverrides(walletConfig)
|
||||||
val km = createNewKeyManager()(newWalletConf)
|
|
||||||
val bip39PasswordOpt = KeyManagerTestUtil.bip39PasswordOpt
|
val bip39PasswordOpt = KeyManagerTestUtil.bip39PasswordOpt
|
||||||
makeDependentFixture(
|
makeDependentFixture(
|
||||||
build = createNewWallet(keyManager = km,
|
build = createNewWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||||
bip39PasswordOpt = bip39PasswordOpt,
|
|
||||||
extraConfig = Some(walletConfig),
|
extraConfig = Some(walletConfig),
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = chainQueryApi),
|
chainQueryApi = chainQueryApi),
|
||||||
|
@ -239,6 +236,17 @@ trait BitcoinSWalletTest
|
||||||
}
|
}
|
||||||
makeDependentFixture(builder, destroy = destroy)(test)
|
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 {
|
object BitcoinSWalletTest extends WalletLogger {
|
||||||
|
@ -293,20 +301,6 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
Future.successful(0)
|
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 {
|
private[bitcoins] class RandomFeeProvider extends FeeRateApi {
|
||||||
// Useful for tests
|
// Useful for tests
|
||||||
var lastFeeRate: Option[FeeUnit] = None
|
var lastFeeRate: Option[FeeUnit] = None
|
||||||
|
@ -324,6 +318,17 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
configs: Vector[Config])(implicit
|
configs: Vector[Config])(implicit
|
||||||
system: ActorSystem): Future[WalletAppConfig] = {
|
system: ActorSystem): Future[WalletAppConfig] = {
|
||||||
import system.dispatcher
|
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 baseConf = BaseWalletTest.getFreshWalletAppConfig(pgUrl, configs)
|
||||||
val walletNameOpt = if (NumberGenerator.bool.sampleSome) {
|
val walletNameOpt = if (NumberGenerator.bool.sampleSome) {
|
||||||
Some(StringGenerators.genNonEmptyString.sampleSome)
|
Some(StringGenerators.genNonEmptyString.sampleSome)
|
||||||
|
@ -341,7 +346,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
case None => baseConf
|
case None => baseConf
|
||||||
}
|
}
|
||||||
|
|
||||||
walletConf.start().map(_ => walletConf)
|
Future.successful(walletConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a function that can be used to create a wallet fixture.
|
/** Returns a function that can be used to create a wallet fixture.
|
||||||
|
@ -351,66 +356,81 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
* or account type.
|
* or account type.
|
||||||
*/
|
*/
|
||||||
private def createNewWallet(
|
private def createNewWallet(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
bip39PasswordOpt: Option[String],
|
bip39PasswordOpt: Option[String],
|
||||||
extraConfig: Option[Config],
|
extraConfig: Option[Config],
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi)(implicit
|
chainQueryApi: ChainQueryApi)(implicit
|
||||||
config: WalletAppConfig,
|
config: WalletAppConfig,
|
||||||
ec: ExecutionContext): () => Future[Wallet] =
|
ec: ExecutionContext): () => Future[Wallet] = { () =>
|
||||||
() => {
|
{
|
||||||
val walletConfig = extraConfig match {
|
val walletConfig = extraConfig match {
|
||||||
case None => config
|
case None => config
|
||||||
case Some(c) => config.withOverrides(c)
|
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
|
// we want to check we're not overwriting
|
||||||
// any user data
|
// any user data
|
||||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
AppConfig.throwIfDefaultDatadir(walletConfig)
|
||||||
|
|
||||||
walletConfig.start().flatMap { _ =>
|
walletConfigWithBip39Pw.start().flatMap { _ =>
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(keyManager,
|
Wallet(nodeApi, chainQueryApi, new RandomFeeProvider)(
|
||||||
nodeApi,
|
walletConfigWithBip39Pw,
|
||||||
chainQueryApi,
|
ec)
|
||||||
new RandomFeeProvider,
|
Wallet.initialize(wallet, bip39PasswordOpt)(walletConfigWithBip39Pw, ec)
|
||||||
keyManager.creationTime)(walletConfig, ec)
|
|
||||||
Wallet.initialize(wallet, bip39PasswordOpt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def createDLCWallet(
|
private def createDLCWallet(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
bip39PasswordOpt: Option[String],
|
bip39PasswordOpt: Option[String],
|
||||||
extraConfig: Option[Config],
|
extraConfig: Option[Config],
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi)(implicit
|
chainQueryApi: ChainQueryApi)(implicit
|
||||||
config: BitcoinSAppConfig,
|
config: BitcoinSAppConfig,
|
||||||
ec: ExecutionContext): Future[DLCWallet] = {
|
ec: ExecutionContext): Future[DLCWallet] = {
|
||||||
val defaultConf = config.walletConf
|
|
||||||
val walletConfig = extraConfig match {
|
val walletConfig = extraConfig match {
|
||||||
case None => defaultConf
|
case None => config
|
||||||
case Some(c) => defaultConf.withOverrides(c)
|
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
|
// we want to check we're not overwriting
|
||||||
// any user data
|
// any user data
|
||||||
AppConfig.throwIfDefaultDatadir(walletConfig)
|
AppConfig.throwIfDefaultDatadir(walletConfigWithBip39Pw.walletConf)
|
||||||
|
|
||||||
val initConfs = for {
|
val initConfs = for {
|
||||||
_ <- walletConfig.start()
|
_ <- walletConfigWithBip39Pw.walletConf.start()
|
||||||
_ <- config.dlcConf.start()
|
_ <- config.dlcConf.start()
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
initConfs.flatMap { _ =>
|
initConfs.flatMap { _ =>
|
||||||
val wallet =
|
val wallet =
|
||||||
DLCWallet(keyManager,
|
DLCWallet(nodeApi, chainQueryApi, new RandomFeeProvider)(
|
||||||
nodeApi,
|
walletConfigWithBip39Pw.walletConf,
|
||||||
chainQueryApi,
|
config.dlcConf,
|
||||||
new RandomFeeProvider,
|
ec)
|
||||||
keyManager.creationTime)(walletConfig, config.dlcConf, ec)
|
|
||||||
Wallet
|
Wallet
|
||||||
.initialize(wallet, bip39PasswordOpt)(walletConfig, ec)
|
.initialize(wallet, bip39PasswordOpt)(
|
||||||
|
walletConfigWithBip39Pw.walletConf,
|
||||||
|
ec)
|
||||||
.map(_.asInstanceOf[DLCWallet])
|
.map(_.asInstanceOf[DLCWallet])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,14 +449,12 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
case Some(walletConf) =>
|
case Some(walletConf) =>
|
||||||
config.withOverrides(walletConf)
|
config.withOverrides(walletConf)
|
||||||
}
|
}
|
||||||
val km =
|
createNewWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||||
createNewKeyManager(bip39PasswordOpt = bip39PasswordOpt)(newWalletConf)
|
extraConfig = extraConfig,
|
||||||
createNewWallet(
|
nodeApi = nodeApi,
|
||||||
keyManager = km,
|
chainQueryApi = chainQueryApi)(newWalletConf,
|
||||||
bip39PasswordOpt = bip39PasswordOpt,
|
ec
|
||||||
extraConfig = extraConfig,
|
)() // get the standard config
|
||||||
nodeApi = nodeApi,
|
|
||||||
chainQueryApi = chainQueryApi)(config, ec)() // get the standard config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a default wallet with bitcoind where the [[ChainQueryApi]] fed to the wallet
|
/** 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
|
//create the wallet with the appropriate callbacks now that
|
||||||
//we have them
|
//we have them
|
||||||
walletWithCallback = Wallet(
|
walletWithCallback = Wallet(
|
||||||
keyManager = wallet.keyManager,
|
|
||||||
nodeApi =
|
nodeApi =
|
||||||
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
|
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind,
|
||||||
feeRateApi = new RandomFeeProvider,
|
feeRateApi = new RandomFeeProvider
|
||||||
creationTime = wallet.keyManager.creationTime
|
|
||||||
)(wallet.walletConfig, wallet.ec)
|
)(wallet.walletConfig, wallet.ec)
|
||||||
//complete the walletCallbackP so we can handle the callbacks when they are
|
//complete the walletCallbackP so we can handle the callbacks when they are
|
||||||
//called without hanging forever.
|
//called without hanging forever.
|
||||||
|
@ -508,15 +524,11 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||||
config: BitcoinSAppConfig,
|
config: BitcoinSAppConfig,
|
||||||
system: ActorSystem): Future[DLCWallet] = {
|
system: ActorSystem): Future[DLCWallet] = {
|
||||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||||
val km =
|
|
||||||
createNewKeyManager(bip39PasswordOpt = bip39PasswordOpt)(
|
|
||||||
config.walletConf)
|
|
||||||
for {
|
for {
|
||||||
wallet <- createDLCWallet(km,
|
wallet <- createDLCWallet(bip39PasswordOpt = bip39PasswordOpt,
|
||||||
bip39PasswordOpt,
|
extraConfig = extraConfig,
|
||||||
extraConfig,
|
nodeApi = nodeApi,
|
||||||
nodeApi,
|
chainQueryApi = chainQueryApi)
|
||||||
chainQueryApi)
|
|
||||||
account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
||||||
newAccountWallet <- wallet.createNewAccount(hdAccount = account1,
|
newAccountWallet <- wallet.createNewAccount(hdAccount = account1,
|
||||||
kmParams =
|
kmParams =
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
package org.bitcoins.wallet
|
package org.bitcoins.wallet
|
||||||
|
|
||||||
import com.typesafe.config.{Config, ConfigFactory}
|
import com.typesafe.config.{Config, ConfigFactory}
|
||||||
import org.bitcoins.commons.serializers.JsonSerializers._
|
|
||||||
import org.bitcoins.core.api.wallet.db._
|
import org.bitcoins.core.api.wallet.db._
|
||||||
import org.bitcoins.core.crypto.{ExtPublicKey, MnemonicCode}
|
import org.bitcoins.core.crypto.{ExtPublicKey, MnemonicCode}
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
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.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||||
import org.bitcoins.feeprovider.ConstantFeeRateProvider
|
import org.bitcoins.feeprovider.ConstantFeeRateProvider
|
||||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
|
||||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
import org.bitcoins.testkit.fixtures.EmptyFixture
|
import org.bitcoins.testkit.fixtures.EmptyFixture
|
||||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
|
@ -26,6 +24,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
|
||||||
class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||||
|
import org.bitcoins.commons.serializers.JsonSerializers._
|
||||||
|
|
||||||
val mnemonic = MnemonicCode.fromWords(
|
val mnemonic = MnemonicCode.fromWords(
|
||||||
Vector(
|
Vector(
|
||||||
|
@ -131,42 +130,35 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||||
lazy val nestedVectors =
|
lazy val nestedVectors =
|
||||||
vectors.filter(_.pathType == HDPurposes.NestedSegWit)
|
vectors.filter(_.pathType == HDPurposes.NestedSegWit)
|
||||||
|
|
||||||
def configForPurpose(purpose: HDPurpose): Config = {
|
def configForPurposeAndSeed(purpose: HDPurpose): Config = {
|
||||||
val purposeStr = purpose match {
|
val purposeStr = purpose match {
|
||||||
case HDPurposes.Legacy => "legacy"
|
case HDPurposes.Legacy => "legacy"
|
||||||
case HDPurposes.SegWit => "segwit"
|
case HDPurposes.SegWit => "segwit"
|
||||||
case HDPurposes.NestedSegWit => "nested-segwit"
|
case HDPurposes.NestedSegWit => "nested-segwit"
|
||||||
case other => fail(s"unexpected purpose: $other")
|
case other => fail(s"unexpected purpose: $other")
|
||||||
}
|
}
|
||||||
|
val entropy = mnemonic.toEntropy.toHex
|
||||||
val confStr = s"""bitcoin-s.wallet.defaultAccountType = $purposeStr
|
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)
|
ConfigFactory.parseString(confStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getWallet(config: WalletAppConfig)(implicit
|
private def getWallet(config: WalletAppConfig)(implicit
|
||||||
ec: ExecutionContext): Future[Wallet] = {
|
ec: ExecutionContext): Future[Wallet] = {
|
||||||
|
import system.dispatcher
|
||||||
val bip39PasswordOpt = None
|
val bip39PasswordOpt = None
|
||||||
val kmE = BIP39KeyManager.initializeWithEntropy(
|
val startedF = config.start()
|
||||||
aesPasswordOpt = config.aesPasswordOpt,
|
for {
|
||||||
entropy = mnemonic.toEntropy,
|
_ <- startedF
|
||||||
bip39PasswordOpt = bip39PasswordOpt,
|
wallet =
|
||||||
kmParams = config.kmParams)
|
Wallet(MockNodeApi,
|
||||||
kmE match {
|
MockChainQueryApi,
|
||||||
case Left(err) =>
|
ConstantFeeRateProvider(SatoshisPerVirtualByte.one))(config, ec)
|
||||||
Future.failed(
|
init <- Wallet.initialize(wallet = wallet,
|
||||||
new RuntimeException(s"Failed to initialize km with err=${err}"))
|
bip39PasswordOpt = bip39PasswordOpt)(config, ec)
|
||||||
case Right(km) =>
|
} yield init
|
||||||
val wallet =
|
|
||||||
Wallet(km,
|
|
||||||
MockNodeApi,
|
|
||||||
MockChainQueryApi,
|
|
||||||
ConstantFeeRateProvider(SatoshisPerVirtualByte.one),
|
|
||||||
TimeUtil.now)(config, ec)
|
|
||||||
val walletF =
|
|
||||||
Wallet.initialize(wallet = wallet,
|
|
||||||
bip39PasswordOpt = bip39PasswordOpt)(config, ec)
|
|
||||||
walletF
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class AccountAndAddrsAndVector(
|
case class AccountAndAddrsAndVector(
|
||||||
|
@ -230,7 +222,7 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||||
}
|
}
|
||||||
|
|
||||||
private def testAccountType(purpose: HDPurpose): Future[Assertion] = {
|
private def testAccountType(purpose: HDPurpose): Future[Assertion] = {
|
||||||
val confOverride = configForPurpose(purpose)
|
val confOverride = configForPurposeAndSeed(purpose)
|
||||||
implicit val conf: WalletAppConfig =
|
implicit val conf: WalletAppConfig =
|
||||||
BitcoinSTestAppConfig.getSpvTestConfig(confOverride).walletConf
|
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.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.script._
|
import org.bitcoins.core.protocol.script._
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerUnlockError
|
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPublicKey}
|
||||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerUnlockError.MnemonicNotFound
|
|
||||||
import org.bitcoins.crypto.{AesPassword, DoubleSha256DigestBE, ECPublicKey}
|
|
||||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.testkitcore.util.TransactionTestUtil._
|
import org.bitcoins.testkitcore.util.TransactionTestUtil._
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
|
@ -126,21 +124,6 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||||
} yield res
|
} 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 =>
|
it should "match block filters" in { wallet: Wallet =>
|
||||||
for {
|
for {
|
||||||
height <- wallet.chainQueryApi.getFilterCount()
|
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.util.{BitcoinScriptUtil, FutureUtil, HDUtil}
|
||||||
import org.bitcoins.core.wallet.builder._
|
import org.bitcoins.core.wallet.builder._
|
||||||
import org.bitcoins.core.wallet.fee._
|
import org.bitcoins.core.wallet.fee._
|
||||||
import org.bitcoins.core.wallet.keymanagement.{
|
import org.bitcoins.core.wallet.keymanagement.{KeyManagerParams}
|
||||||
KeyManagerParams,
|
|
||||||
KeyManagerUnlockError
|
|
||||||
}
|
|
||||||
import org.bitcoins.core.wallet.utxo.TxoState._
|
import org.bitcoins.core.wallet.utxo.TxoState._
|
||||||
import org.bitcoins.core.wallet.utxo._
|
import org.bitcoins.core.wallet.utxo._
|
||||||
import org.bitcoins.crypto._
|
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.config.WalletAppConfig
|
||||||
import org.bitcoins.wallet.internal._
|
import org.bitcoins.wallet.internal._
|
||||||
import org.bitcoins.wallet.models._
|
import org.bitcoins.wallet.models._
|
||||||
|
@ -54,7 +51,9 @@ abstract class Wallet
|
||||||
with RescanHandling
|
with RescanHandling
|
||||||
with WalletLogger {
|
with WalletLogger {
|
||||||
|
|
||||||
override def keyManager: BIP39KeyManager
|
override def keyManager: BIP39KeyManager = {
|
||||||
|
walletConfig.kmConf.toBip39KeyManager
|
||||||
|
}
|
||||||
|
|
||||||
implicit val ec: ExecutionContext
|
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] =
|
override def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
||||||
for {
|
for {
|
||||||
_ <- nodeApi.broadcastTransaction(transaction)
|
_ <- nodeApi.broadcastTransaction(transaction)
|
||||||
|
@ -962,25 +938,21 @@ abstract class Wallet
|
||||||
object Wallet extends WalletLogger {
|
object Wallet extends WalletLogger {
|
||||||
|
|
||||||
private case class WalletImpl(
|
private case class WalletImpl(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi
|
||||||
override val creationTime: Instant
|
|
||||||
)(implicit
|
)(implicit
|
||||||
val walletConfig: WalletAppConfig,
|
val walletConfig: WalletAppConfig,
|
||||||
val ec: ExecutionContext
|
val ec: ExecutionContext
|
||||||
) extends Wallet
|
) extends Wallet
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
keyManager: BIP39KeyManager,
|
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi)(implicit
|
||||||
creationTime: Instant)(implicit
|
|
||||||
config: WalletAppConfig,
|
config: WalletAppConfig,
|
||||||
ec: ExecutionContext): Wallet = {
|
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 */
|
/** 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.api.node.NodeApi
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.util.Mutable
|
import org.bitcoins.core.util.Mutable
|
||||||
import org.bitcoins.core.wallet.keymanagement.{
|
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||||
KeyManagerInitializeError,
|
|
||||||
KeyManagerParams
|
|
||||||
}
|
|
||||||
import org.bitcoins.crypto.AesPassword
|
import org.bitcoins.crypto.AesPassword
|
||||||
import org.bitcoins.db.DatabaseDriver.{PostgreSQL, SQLite}
|
import org.bitcoins.db.DatabaseDriver.{PostgreSQL, SQLite}
|
||||||
import org.bitcoins.db._
|
import org.bitcoins.db._
|
||||||
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
|
|
||||||
import org.bitcoins.keymanager.config.KeyManagerAppConfig
|
import org.bitcoins.keymanager.config.KeyManagerAppConfig
|
||||||
import org.bitcoins.tor.config.TorAppConfig
|
import org.bitcoins.tor.config.TorAppConfig
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig.RebroadcastTransactionsRunnable
|
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 org.bitcoins.wallet.{Wallet, WalletCallbacks, WalletLogger}
|
||||||
|
|
||||||
import java.nio.file.{Files, Path, Paths}
|
import java.nio.file.{Files, Path, Paths}
|
||||||
import java.util.concurrent.{
|
import java.util.concurrent._
|
||||||
ExecutorService,
|
|
||||||
Executors,
|
|
||||||
ScheduledExecutorService,
|
|
||||||
ScheduledFuture,
|
|
||||||
ThreadFactory,
|
|
||||||
TimeUnit
|
|
||||||
}
|
|
||||||
import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}
|
import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration}
|
||||||
import scala.concurrent.{Await, ExecutionContext, Future}
|
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||||
|
|
||||||
|
@ -182,6 +171,7 @@ case class WalletAppConfig(
|
||||||
override def start(): Future[Unit] = {
|
override def start(): Future[Unit] = {
|
||||||
for {
|
for {
|
||||||
_ <- super.start()
|
_ <- super.start()
|
||||||
|
_ <- kmConf.start()
|
||||||
} yield {
|
} yield {
|
||||||
logger.debug(s"Initializing wallet setup")
|
logger.debug(s"Initializing wallet setup")
|
||||||
|
|
||||||
|
@ -343,42 +333,17 @@ object WalletAppConfig
|
||||||
walletConf: WalletAppConfig,
|
walletConf: WalletAppConfig,
|
||||||
ec: ExecutionContext): Future[Wallet] = {
|
ec: ExecutionContext): Future[Wallet] = {
|
||||||
walletConf.hasWallet().flatMap { walletExists =>
|
walletConf.hasWallet().flatMap { walletExists =>
|
||||||
val aesPasswordOpt = walletConf.aesPasswordOpt
|
|
||||||
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
||||||
|
|
||||||
if (walletExists) {
|
if (walletExists) {
|
||||||
logger.info(s"Using pre-existing wallet")
|
logger.info(s"Using pre-existing wallet")
|
||||||
// TODO change me when we implement proper password handling
|
val wallet =
|
||||||
BIP39LockedKeyManager.unlock(aesPasswordOpt,
|
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
||||||
bip39PasswordOpt,
|
Future.successful(wallet)
|
||||||
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}")
|
|
||||||
}
|
|
||||||
} else {
|
} 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")
|
logger.info(s"Creating new wallet")
|
||||||
val unInitializedWallet =
|
val unInitializedWallet =
|
||||||
Wallet(keyManager,
|
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
||||||
nodeApi,
|
|
||||||
chainQueryApi,
|
|
||||||
feeRateApi,
|
|
||||||
keyManager.creationTime)
|
|
||||||
|
|
||||||
Wallet.initialize(wallet = unInitializedWallet,
|
Wallet.initialize(wallet = unInitializedWallet,
|
||||||
bip39PasswordOpt = bip39PasswordOpt)
|
bip39PasswordOpt = bip39PasswordOpt)
|
||||||
|
|
Loading…
Add table
Reference in a new issue