2024 08 07 createnewaccount rpc (#5638)

* Implement creatnewaccount rpc

* Get WalletRoutesSpec createnewaccount unit test passing

* Rename HDPurposes -> HDPurpose

* Fix docs

* Implement ConsoleCli arg, change HDPurpose json serialization to be a json string instead of a num
This commit is contained in:
Chris Stewart 2024-08-13 08:32:03 -07:00 committed by GitHub
parent 9c25209198
commit 41fab3dfd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 329 additions and 171 deletions

View File

@ -2,26 +2,26 @@ package org.bitcoins.commons.rpc
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter
import org.bitcoins.commons.jsonmodels.cli.ContractDescriptorParser
import org.bitcoins.commons.serializers.JsonReaders
import org.bitcoins.commons.serializers.{JsonReaders, Picklers}
import org.bitcoins.commons.util.{BitcoinSLogger, WalletNames}
import org.bitcoins.core.api.dlc.wallet.db.DLCContactDb
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.crypto.{ExtPrivateKey, MnemonicCode}
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.hd.{AddressType, HDPurpose}
import org.bitcoins.core.hd.AddressType.SegWit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.BlockStamp.BlockHeight
import org.bitcoins.core.protocol.dlc.models.ContractDescriptor
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint}
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.AddressLabelTag
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
import scodec.bits.ByteVector
import ujson._
import ujson.*
import java.net.{InetSocketAddress, URI}
import java.nio.file.Path
@ -1803,6 +1803,25 @@ object LoadWallet extends ServerJsonModels with BitcoinSLogger {
}
}
case class CreateNewAccount(purpose: HDPurpose)
extends CommandRpc
with AppServerCliCommand
object CreateNewAccount {
def fromJsArr(arr: ujson.Arr): Try[CreateNewAccount] = {
arr.arr.toVector match {
case purposeJs +: _ =>
Try(upickle.default.read(purposeJs)(Picklers.hdPurpose))
.map(CreateNewAccount.apply)
case _ =>
val exn = new IllegalArgumentException(
s"Invalid input for createnewaccount rpc, got=$arr")
Failure(exn)
}
}
}
trait ServerJsonModels {
def jsToOracleAnnouncementTLV(js: Value): OracleAnnouncementTLV =

View File

@ -9,22 +9,22 @@ import org.bitcoins.core.api.dlc.wallet.db.{DLCContactDb, IncomingDLCOfferDb}
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
import org.bitcoins.core.config.DLC
import org.bitcoins.core.crypto._
import org.bitcoins.core.crypto.*
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
import org.bitcoins.core.gcs.FilterType
import org.bitcoins.core.hd.{AddressType, HDPath}
import org.bitcoins.core.hd.{AddressType, HDPath, HDPurpose}
import org.bitcoins.core.number.{Int32, UInt16, UInt32, UInt64}
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.dlc.models.DLCStatus._
import org.bitcoins.core.protocol.dlc.models._
import org.bitcoins.core.protocol.dlc.models.DLCStatus.*
import org.bitcoins.core.protocol.dlc.models.*
import org.bitcoins.core.protocol.script.{
ScriptPubKey,
ScriptWitness,
ScriptWitnessV0,
WitnessScriptPubKey
}
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionOutPoint,
@ -35,14 +35,14 @@ import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.serializers.PicklerKeys
import org.bitcoins.core.util.{NetworkUtil, TimeUtil}
import org.bitcoins.core.util.TimeUtil._
import org.bitcoins.core.util.TimeUtil.*
import org.bitcoins.core.util.sorted.OrderedSchnorrSignatures
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte}
import org.bitcoins.core.wallet.utxo.{AddressLabelTag, TxoState}
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
import scodec.bits.ByteVector
import ujson._
import upickle.default._
import ujson.*
import upickle.default.*
import java.io.File
import java.net.{InetSocketAddress, URI}
@ -1767,4 +1767,9 @@ object Picklers {
SatoshisPerVirtualByte.fromLong(value.num.toLong)
}
implicit val hdPurpose: ReadWriter[HDPurpose] = {
readwriter[ujson.Str]
.bimap(_.toString, str => HDPurpose.fromString(str.str))
}
}

View File

@ -1,20 +1,20 @@
package org.bitcoins.cli
import org.bitcoins.cli.CliCommand._
import org.bitcoins.cli.CliReaders._
import org.bitcoins.cli.CliCommand.*
import org.bitcoins.cli.CliReaders.*
import org.bitcoins.cli.ConsoleCli.RequestParam
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter
import org.bitcoins.commons.rpc._
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.commons.rpc.*
import org.bitcoins.commons.serializers.Picklers.*
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.crypto.*
import org.bitcoins.core.currency.*
import org.bitcoins.core.hd.{AddressType, HDPurpose}
import org.bitcoins.core.hd.AddressType.SegWit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.core.protocol.transaction.{
EmptyTransaction,
Transaction,
@ -24,15 +24,15 @@ import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.AddressLabelTag
import org.bitcoins.crypto._
import org.bitcoins.crypto.*
import scodec.bits.ByteVector
import scopt.OParser
import sttp.client3.logging.LogLevel
import sttp.client3.logging.slf4j.Slf4jLoggingBackend
import sttp.client3.{Identity, SttpBackend}
import sttp.model.StatusCode
import ujson._
import upickle.{default => up}
import ujson.*
import upickle.default as up
import java.io.File
import java.net.InetSocketAddress
@ -267,8 +267,19 @@ object ConsoleCli extends BitcoinSLogger {
.action((_, conf) => conf.copy(command = GetAccounts))
.text("Returns list of all wallet accounts"),
cmd("createnewaccount")
.action((_, conf) => conf.copy(command = CreateNewAccount))
.text("Creates a new wallet account"),
.action((_, conf) => conf.copy(command = CreateNewAccount(null)))
.text("Creates a new wallet account")
.children(
arg[String]("hd_purpose")
.text("hd_purpose according to BIP43")
.required()
.action((purpose, conf) =>
conf.copy(command = conf.command match {
case c: CreateNewAccount =>
c.copy(purpose = HDPurpose.fromString(purpose))
case other => other
}))
),
cmd("getaddressinfo")
.action((_, conf) => conf.copy(command = GetAddressInfo(null)))
.text("Returns list of all wallet accounts")
@ -2160,8 +2171,8 @@ object CliCommand {
RequestParam("getunusedaddresses")
case GetAccounts =>
RequestParam("getaccounts")
case CreateNewAccount =>
RequestParam("createnewaccount")
case CreateNewAccount(purpose) =>
RequestParam("createnewaccount", Seq(up.writeJs(purpose)))
case IsEmpty =>
RequestParam("isempty")
case WalletInfo =>
@ -2668,7 +2679,6 @@ object CliCommand {
case object GetFundedAddresses extends AppServerCliCommand
case object GetUnusedAddresses extends AppServerCliCommand
case object GetAccounts extends AppServerCliCommand
case object CreateNewAccount extends AppServerCliCommand
case object IsEmpty extends AppServerCliCommand
case object WalletInfo extends AppServerCliCommand
case object ListWallets extends AppServerCliCommand

View File

@ -647,8 +647,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
val accountDb =
AccountDb(
xpub = xpub,
hdAccount =
HDAccount(HDCoin(HDPurposes.Legacy, HDCoinType.Testnet), 0)
hdAccount = HDAccount(HDCoin(HDPurpose.Legacy, HDCoinType.Testnet), 0)
)
(() => mockWalletApi.listAccounts())

View File

@ -1,13 +1,18 @@
package org.bitcoins.server
import org.apache.pekko.http.scaladsl.model.ContentTypes._
import org.apache.pekko.http.scaladsl.model.ContentTypes.*
import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest
import org.bitcoins.commons.serializers.Picklers
import org.bitcoins.core.api.chain.ChainApi
import org.bitcoins.core.api.wallet.db.AccountDb
import org.bitcoins.core.crypto.ExtKeyVersion.SegWitMainNetPriv
import org.bitcoins.core.crypto.ExtPrivateKey
import org.bitcoins.core.hd.{HDAccount, HDPurpose}
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.dlc.models.DLCMessage.{DLCAccept, DLCOffer}
import org.bitcoins.core.protocol.dlc.models.DLCStatus
import org.bitcoins.core.protocol.tlv.{DLCOfferTLV, LnMessage, LnMessageFactory}
import org.bitcoins.core.wallet.fee.{SatoshisPerVirtualByte}
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.crypto.Sha256Digest
import org.bitcoins.feeprovider.ConstantFeeRateProvider
import org.bitcoins.node.Node
@ -155,6 +160,37 @@ class WalletRoutesSpec
)
}
}
"createnewaccount" in {
val keyVersion = SegWitMainNetPriv
val extPrivKey = ExtPrivateKey.freshRootKey(keyVersion)
val extPubKey = extPrivKey.extPublicKey
val hdAccount = HDAccount.fromExtKeyVersion(version = keyVersion, idx = 0)
val accountDb = AccountDb(extPubKey, hdAccount = hdAccount)
(mockWalletApi
.createNewAccount(_: HDPurpose))
.expects(HDPurpose.default)
.returning(Future.successful(mockWalletApi))
(() => mockWalletApi.listAccounts())
.expects()
.returning(Future.successful(Vector(accountDb)))
val cmd = ServerCommand(
"createnewaccount",
ujson.Arr(
upickle.default.writeJs(HDPurpose.default)(Picklers.hdPurpose))
)
val route = walletRoutes.handleCommand(cmd)
Get() ~> route ~> check {
assert(contentType == `application/json`)
val response = responseAs[String]
assert(
response == s"""{"result":["${extPubKey.toString}"],"error":null}"""
)
}
}
}
}

View File

@ -5,12 +5,13 @@ import org.apache.pekko.http.scaladsl.model.HttpEntity
import org.apache.pekko.http.scaladsl.server.Directives.complete
import org.apache.pekko.http.scaladsl.server.Route
import org.apache.pekko.stream.Materializer
import org.bitcoins.commons.rpc._
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.commons.rpc.*
import org.bitcoins.commons.serializers.Picklers
import org.bitcoins.commons.serializers.Picklers.*
import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
import org.bitcoins.core.currency._
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.currency.*
import org.bitcoins.core.protocol.tlv.*
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte}
import org.bitcoins.core.wallet.rescan.RescanState
@ -21,13 +22,13 @@ import org.bitcoins.core.wallet.utxo.{
TxoState
}
import org.bitcoins.crypto.NetworkElement
import org.bitcoins.keymanager._
import org.bitcoins.keymanager.*
import org.bitcoins.keymanager.config.KeyManagerAppConfig
import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute}
import org.bitcoins.wallet.WalletHolder
import org.bitcoins.wallet.config.WalletAppConfig
import ujson._
import upickle.default._
import ujson.*
import upickle.default.*
import java.nio.file.{Files, Path}
import java.time.Instant
@ -861,16 +862,20 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
}
}
case ServerCommand("createnewaccount", _) =>
complete {
for {
newWallet <- wallet.createNewAccount(
wallet.keyManager.kmParams.purpose)
accounts <- newWallet.listAccounts()
} yield {
val xpubs = accounts.map(_.xpub)
Server.httpSuccess(xpubs)
}
case ServerCommand("createnewaccount", arr) =>
withValidServerCommand(CreateNewAccount.fromJsArr(arr)) {
case CreateNewAccount(purpose) =>
complete {
for {
newWallet <- wallet.createNewAccount(purpose)
accounts <- newWallet.listAccounts()
} yield {
val xpubs = accounts.map(_.xpub)
val json =
xpubs.map(upickle.default.writeJs(_)(Picklers.extPubKeyPickler))
Server.httpSuccess(Arr.from(json))
}
}
}
case ServerCommand("keymanagerpassphrasechange", arr) =>

View File

@ -692,6 +692,7 @@ lazy val testkit = project
coreJVM % testAndCompile,
appServer,
chain,
cli,
bitcoindRpc,
eclairRpc,
lndRpc,

View File

@ -1,7 +1,7 @@
package org.bitcoins.core.crypto.bip32
import org.bitcoins.core.crypto.{ExtKey, ExtPublicKey}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path, HardenedType}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path, HDPurpose, HardenedType}
import org.bitcoins.testkitcore.gen.{
CryptoGenerators,
HDGenerators,
@ -9,7 +9,7 @@ import org.bitcoins.testkitcore.gen.{
}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
import org.scalacheck.Gen
import scodec.bits._
import scodec.bits.*
import scala.util.{Success, Try}
@ -287,4 +287,12 @@ class BIP32PathTest extends BitcoinSUnitTest {
BIP32Path.fromHardenedString(badPath4)
}
}
it must "have serialization symmetry for HDPurpose" in {
for (p <- HDPurpose.all) {
val fromString = HDPurpose.fromString(p.toString)
assert(fromString == p)
assert(p.toString == fromString.toString)
}
}
}

View File

@ -198,7 +198,7 @@ class HDPathTest extends BitcoinSUnitTest {
val firstString = " m / 44' / 0' / 0' / 0 / 0 "
val first = LegacyHDPath.fromString(firstString)
assert(first.purpose == HDPurposes.Legacy)
assert(first.purpose == HDPurpose.Legacy)
assert(first.coin.coinType == HDCoinType.Bitcoin)
assert(first.account.index == 0)
assert(first.chain.chainType == HDChainType.External)
@ -207,7 +207,7 @@ class HDPathTest extends BitcoinSUnitTest {
val secondString = " m / 44' / 0' / 0' / 0 / 1 "
val second = LegacyHDPath.fromString(secondString)
assert(second.purpose == HDPurposes.Legacy)
assert(second.purpose == HDPurpose.Legacy)
assert(second.coin.coinType == HDCoinType.Bitcoin)
assert(second.account.index == 0)
assert(second.chain.chainType == HDChainType.External)
@ -216,7 +216,7 @@ class HDPathTest extends BitcoinSUnitTest {
val thirdString = " m / 44' / 0' / 0' / 1 / 0 "
val third = LegacyHDPath.fromString(thirdString)
assert(third.purpose == HDPurposes.Legacy)
assert(third.purpose == HDPurpose.Legacy)
assert(third.coin.coinType == HDCoinType.Bitcoin)
assert(third.account.index == 0)
assert(third.chain.chainType == HDChainType.Change)
@ -225,7 +225,7 @@ class HDPathTest extends BitcoinSUnitTest {
val fourthString = " m / 44' / 0' / 0' / 1 / 1 "
val fourth = LegacyHDPath.fromString(fourthString)
assert(fourth.purpose == HDPurposes.Legacy)
assert(fourth.purpose == HDPurpose.Legacy)
assert(fourth.coin.coinType == HDCoinType.Bitcoin)
assert(fourth.account.index == 0)
assert(fourth.chain.chainType == HDChainType.Change)
@ -234,7 +234,7 @@ class HDPathTest extends BitcoinSUnitTest {
val fifthString = " m / 44' / 0' / 1' / 0 / 0 "
val fifth = LegacyHDPath.fromString(fifthString)
assert(fifth.purpose == HDPurposes.Legacy)
assert(fifth.purpose == HDPurpose.Legacy)
assert(fifth.coin.coinType == HDCoinType.Bitcoin)
assert(fifth.account.index == 1)
assert(fifth.chain.chainType == HDChainType.External)
@ -243,7 +243,7 @@ class HDPathTest extends BitcoinSUnitTest {
val sixthString = " m / 44' / 0' / 1' / 0 / 1 "
val sixth = LegacyHDPath.fromString(sixthString)
assert(sixth.purpose == HDPurposes.Legacy)
assert(sixth.purpose == HDPurpose.Legacy)
assert(sixth.coin.coinType == HDCoinType.Bitcoin)
assert(sixth.account.index == 1)
assert(sixth.chain.chainType == HDChainType.External)
@ -252,7 +252,7 @@ class HDPathTest extends BitcoinSUnitTest {
val seventhString = " m / 44' / 0' / 1' / 1 / 0 "
val seventh = LegacyHDPath.fromString(seventhString)
assert(seventh.purpose == HDPurposes.Legacy)
assert(seventh.purpose == HDPurpose.Legacy)
assert(seventh.coin.coinType == HDCoinType.Bitcoin)
assert(seventh.account.index == 1)
assert(seventh.chain.chainType == HDChainType.Change)
@ -261,7 +261,7 @@ class HDPathTest extends BitcoinSUnitTest {
val eightString = " m / 44' / 0' / 1' / 1 / 1 "
val eigth = LegacyHDPath.fromString(eightString)
assert(eigth.purpose == HDPurposes.Legacy)
assert(eigth.purpose == HDPurpose.Legacy)
assert(eigth.coin.coinType == HDCoinType.Bitcoin)
assert(eigth.account.index == 1)
assert(eigth.chain.chainType == HDChainType.Change)
@ -270,7 +270,7 @@ class HDPathTest extends BitcoinSUnitTest {
val ninthString = " m / 44' / 1' / 0' / 0 / 1 "
val ninth = LegacyHDPath.fromString(ninthString)
assert(ninth.purpose == HDPurposes.Legacy)
assert(ninth.purpose == HDPurpose.Legacy)
assert(ninth.coin.coinType == HDCoinType.Testnet)
assert(ninth.account.index == 0)
assert(ninth.chain.chainType == HDChainType.External)
@ -279,7 +279,7 @@ class HDPathTest extends BitcoinSUnitTest {
val tenthString = " m / 44' / 1' / 0' / 0 / 1 "
val tenth = LegacyHDPath.fromString(tenthString)
assert(tenth.purpose == HDPurposes.Legacy)
assert(tenth.purpose == HDPurpose.Legacy)
assert(tenth.coin.coinType == HDCoinType.Testnet)
assert(tenth.account.index == 0)
assert(tenth.chain.chainType == HDChainType.External)
@ -288,7 +288,7 @@ class HDPathTest extends BitcoinSUnitTest {
val eleventhString = " m / 44' / 1' / 0' / 1 / 0 "
val eleventh = LegacyHDPath.fromString(eleventhString)
assert(eleventh.purpose == HDPurposes.Legacy)
assert(eleventh.purpose == HDPurpose.Legacy)
assert(eleventh.coin.coinType == HDCoinType.Testnet)
assert(eleventh.account.index == 0)
assert(eleventh.chain.chainType == HDChainType.Change)
@ -297,7 +297,7 @@ class HDPathTest extends BitcoinSUnitTest {
val twelfthString = " m / 44' / 1' / 0' / 1 / 1 "
val twelfth = LegacyHDPath.fromString(twelfthString)
assert(twelfth.purpose == HDPurposes.Legacy)
assert(twelfth.purpose == HDPurpose.Legacy)
assert(twelfth.coin.coinType == HDCoinType.Testnet)
assert(twelfth.account.index == 0)
assert(twelfth.chain.chainType == HDChainType.Change)
@ -306,7 +306,7 @@ class HDPathTest extends BitcoinSUnitTest {
val thirteenthString = " m / 44' / 1' / 1' / 0 / 0 "
val thirteenth = LegacyHDPath.fromString(thirteenthString)
assert(thirteenth.purpose == HDPurposes.Legacy)
assert(thirteenth.purpose == HDPurpose.Legacy)
assert(thirteenth.coin.coinType == HDCoinType.Testnet)
assert(thirteenth.account.index == 1)
assert(thirteenth.chain.chainType == HDChainType.External)
@ -315,7 +315,7 @@ class HDPathTest extends BitcoinSUnitTest {
val fourteenthString = " m / 44' / 1' / 1' / 0 / 1 "
val fourteenth = LegacyHDPath.fromString(fourteenthString)
assert(fourteenth.purpose == HDPurposes.Legacy)
assert(fourteenth.purpose == HDPurpose.Legacy)
assert(fourteenth.coin.coinType == HDCoinType.Testnet)
assert(fourteenth.account.index == 1)
assert(fourteenth.chain.chainType == HDChainType.External)
@ -324,7 +324,7 @@ class HDPathTest extends BitcoinSUnitTest {
val fifteenthString = " m / 44' / 1' / 1' / 1 / 0 "
val fifteenth = LegacyHDPath.fromString(fifteenthString)
assert(fifteenth.purpose == HDPurposes.Legacy)
assert(fifteenth.purpose == HDPurpose.Legacy)
assert(fifteenth.coin.coinType == HDCoinType.Testnet)
assert(fifteenth.account.index == 1)
assert(fifteenth.chain.chainType == HDChainType.Change)
@ -333,7 +333,7 @@ class HDPathTest extends BitcoinSUnitTest {
val sixteenthString = " m / 44' / 1' / 1' / 1 / 1 "
val sixteenth = LegacyHDPath.fromString(sixteenthString)
assert(sixteenth.purpose == HDPurposes.Legacy)
assert(sixteenth.purpose == HDPurpose.Legacy)
assert(sixteenth.coin.coinType == HDCoinType.Testnet)
assert(sixteenth.account.index == 1)
assert(sixteenth.chain.chainType == HDChainType.Change)

View File

@ -25,7 +25,7 @@ case class AddressRecord(
def toAddressDb(scriptPubKey: ScriptPubKey): AddressDb = {
(purpose, address, scriptWitnessOpt) match {
case (HDPurposes.SegWit, bechAddr: Bech32Address, Some(scriptWitness)) =>
case (HDPurpose.SegWit, bechAddr: Bech32Address, Some(scriptWitness)) =>
val path =
SegWitHDPath(coinType = accountCoin,
accountIndex = accountIndex,
@ -39,7 +39,7 @@ case class AddressRecord(
witnessScript = scriptWitness,
scriptPubKey = scriptPubKey)
case (HDPurposes.Legacy, legacyAddr: P2PKHAddress, None) =>
case (HDPurpose.Legacy, legacyAddr: P2PKHAddress, None) =>
val path = LegacyHDPath(coinType = accountCoin,
accountIndex = accountIndex,
chainType = accountChain,
@ -50,7 +50,7 @@ case class AddressRecord(
legacyAddr,
scriptPubKey = scriptPubKey)
case (HDPurposes.NestedSegWit,
case (HDPurpose.NestedSegWit,
address: P2SHAddress,
Some(scriptWitness)) =>
val path = NestedSegWitHDPath(coinType = accountCoin,

View File

@ -1,9 +1,12 @@
package org.bitcoins.core.crypto
import org.bitcoins.core.hd.HDCoinType
import org.bitcoins.crypto.{Factory, NetworkElement}
import scodec.bits._
import scodec.bits.*
sealed abstract class ExtKeyVersion extends NetworkElement
sealed abstract class ExtKeyVersion extends NetworkElement {
def hdCoinType: HDCoinType
}
sealed abstract class ExtKeyPrivVersion extends ExtKeyVersion
@ -43,6 +46,8 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object LegacyMainNetPriv extends ExtKeyPrivVersion {
override val bytes = hex"0x0488ADE4"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
/** Generating a [[org.bitcoins.core.crypto.ExtPrivateKey ExtPrivateKey]] with
@ -50,6 +55,7 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object LegacyTestNet3Priv extends ExtKeyPrivVersion {
override val bytes = hex"0x04358394"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
/** Generating a [[org.bitcoins.core.crypto.ExtPrivateKey ExtPrivateKey]] with
@ -57,6 +63,7 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object SegWitMainNetPriv extends ExtKeyPrivVersion {
override val bytes = hex"0x04b2430c"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
/** Generating a [[org.bitcoins.core.crypto.ExtPrivateKey ExtPrivateKey]] with
@ -64,6 +71,7 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object SegWitTestNet3Priv extends ExtKeyPrivVersion {
override val bytes = hex"0x045f18bc"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
/** Generating a [[org.bitcoins.core.crypto.ExtPrivateKey ExtPrivateKey]] with
@ -74,6 +82,7 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object NestedSegWitMainNetPriv extends ExtKeyPrivVersion {
override val bytes = hex"0x049D7878"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
/** Generating a [[org.bitcoins.core.crypto.ExtPrivateKey ExtPrivateKey]] with
@ -84,6 +93,7 @@ object ExtKeyVersion extends Factory[ExtKeyVersion] {
*/
case object NestedSegWitTestNet3Priv extends ExtKeyPrivVersion {
override val bytes = hex"0x044a4e28"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
}
@ -103,6 +113,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
*/
case object LegacyMainNetPub extends ExtKeyPubVersion {
override val bytes = hex"0x0488b21E"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
/** Generating a [[org.bitcoins.core.crypto.ExtPublicKey ExtPublicKey]] with
@ -117,6 +128,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
// Value stolen from Trezor lib, see link above
// ByteVector.fromLong(71979618).toHex = 00000000044a5262
override val bytes = hex"0x044a5262"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
/** Generating a [[org.bitcoins.core.crypto.ExtPublicKey ExtPublicKey]] with
@ -127,6 +139,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
*/
case object NestedSegWitMainNetPub extends ExtKeyPubVersion {
override val bytes = hex"0x049D7CB2"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
/** Generating a [[org.bitcoins.core.crypto.ExtPublicKey ExtPublicKey]] with
@ -134,6 +147,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
*/
case object SegWitTestNet3Pub extends ExtKeyPubVersion {
override val bytes = hex"0x045f1cf6"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
/** Generating a [[org.bitcoins.core.crypto.ExtPublicKey ExtPublicKey]] with
@ -141,6 +155,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
*/
case object LegacyTestNet3Pub extends ExtKeyPubVersion {
override val bytes = hex"0x043587CF"
override def hdCoinType: HDCoinType = HDCoinType.Testnet
}
/** Generating a [[org.bitcoins.core.crypto.ExtPublicKey ExtPublicKey]] with
@ -148,6 +163,7 @@ object ExtKeyPubVersion extends Factory[ExtKeyPubVersion] {
*/
case object SegWitMainNetPub extends ExtKeyPubVersion {
override val bytes = hex"0x04b24746"
override def hdCoinType: HDCoinType = HDCoinType.Bitcoin
}
override def fromBytes(bytes: ByteVector): ExtKeyPubVersion = {

View File

@ -201,6 +201,15 @@ case class BIP32Node(index: Int, hardenedOpt: Option[HardenedType]) {
else UInt32(index)
}
object BIP32Node extends StringFactory[BIP32Node] {
override def fromString(string: String): BIP32Node = {
val path = BIP32Path.fromString(string)
require(path.length == 1,
s"BIP32Node can only have one element in the path, got=$path")
BIP32Node(path.head.index, path.head.hardenedOpt)
}
}
sealed abstract class HardenedType
object HardenedType extends StringFactory[HardenedType] {

View File

@ -1,5 +1,7 @@
package org.bitcoins.core.hd
import org.bitcoins.core.crypto.ExtKeyVersion
/** Represents a
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#Account BIP44]],
* [[https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki BIP84]] and
@ -63,4 +65,9 @@ object HDAccount {
def isSameAccount(bip32Path: BIP32Path, account: HDAccount): Boolean = {
isSameAccount(bip32Path.toVector, account)
}
def fromExtKeyVersion(version: ExtKeyVersion, idx: Int): HDAccount = {
val coin = HDCoin.fromExtKeyVersion(version)
HDAccount(coin, idx)
}
}

View File

@ -19,9 +19,9 @@ sealed abstract class HDAddress extends BIP32Path {
def toPath: HDPath =
purpose match {
case HDPurposes.Legacy => LegacyHDPath(this)
case HDPurposes.SegWit => SegWitHDPath(this)
case HDPurposes.NestedSegWit => NestedSegWitHDPath(this)
case HDPurpose.Legacy => LegacyHDPath(this)
case HDPurpose.SegWit => SegWitHDPath(this)
case HDPurpose.NestedSegWit => NestedSegWitHDPath(this)
case unknown: HDPurpose =>
throw new IllegalArgumentException(s"Unknown HD purpose $unknown")
}

View File

@ -1,6 +1,25 @@
package org.bitcoins.core.hd
import org.bitcoins.core.crypto.ExtKeyPubVersion.{
LegacyMainNetPub,
LegacyTestNet3Pub,
NestedSegWitMainNetPub,
NestedSegWitTestNet3Pub,
SegWitMainNetPub,
SegWitTestNet3Pub
}
import org.bitcoins.core.crypto.ExtKeyVersion
import org.bitcoins.core.crypto.ExtKeyVersion.{
LegacyMainNetPriv,
LegacyTestNet3Priv,
NestedSegWitMainNetPriv,
NestedSegWitTestNet3Priv,
SegWitMainNetPriv,
SegWitTestNet3Priv
}
/** Contains the path m / purpose' / coin_type' /
*
* @see
* https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#path-levels
*/
@ -16,7 +35,7 @@ object HDCoin {
def fromPath(path: BIP32Path): Option[HDCoin] = {
if (path.path.length == 2) {
HDPurposes.fromNode(path.path.head).map { purpose =>
HDPurpose.fromNode(path.path.head).map { purpose =>
val coinType = HDCoinType.fromInt(path.path.last.index)
HDCoin(purpose, coinType)
@ -25,4 +44,18 @@ object HDCoin {
None
}
}
def fromExtKeyVersion(version: ExtKeyVersion): HDCoin = {
version match {
case SegWitMainNetPriv | SegWitMainNetPub | SegWitTestNet3Priv |
SegWitTestNet3Pub =>
HDCoin(HDPurpose.SegWit, version.hdCoinType)
case NestedSegWitMainNetPriv | NestedSegWitMainNetPub |
NestedSegWitTestNet3Priv | NestedSegWitTestNet3Pub =>
HDCoin(HDPurpose.NestedSegWit, version.hdCoinType)
case LegacyMainNetPriv | LegacyMainNetPub | LegacyTestNet3Priv |
LegacyTestNet3Pub =>
HDCoin(HDPurpose.Legacy, version.hdCoinType)
}
}
}

View File

@ -44,12 +44,12 @@ private[hd] trait HDPathFactory[PathType <: BIP32Path]
case BIP32Node(_, None) =>
throw new IllegalArgumentException(
"The first child in a HD path must be hardened")
case BIP32Node(HDPurposes.Legacy.constant, Some(_)) => HDPurposes.Legacy
case BIP32Node(HDPurposes.SegWit.constant, Some(_)) => HDPurposes.SegWit
case BIP32Node(HDPurposes.NestedSegWit.constant, Some(_)) =>
HDPurposes.NestedSegWit
case BIP32Node(HDPurposes.Multisig.constant, Some(_)) =>
HDPurposes.Multisig
case BIP32Node(HDPurpose.Legacy.constant, Some(_)) => HDPurpose.Legacy
case BIP32Node(HDPurpose.SegWit.constant, Some(_)) => HDPurpose.SegWit
case BIP32Node(HDPurpose.NestedSegWit.constant, Some(_)) =>
HDPurpose.NestedSegWit
case BIP32Node(HDPurpose.Multisig.constant, Some(_)) =>
HDPurpose.Multisig
case BIP32Node(unknown, Some(_)) =>
throw new IllegalArgumentException(
s"Purpose constant ($unknown) is not a known purpose constant")
@ -103,7 +103,7 @@ private[hd] trait HDPathFactory[PathType <: BIP32Path]
def PURPOSE: Int
protected lazy val hdPurpose: HDPurpose =
HDPurposes.fromConstant(PURPOSE).get // todo
HDPurpose.fromConstant(PURPOSE).get // todo
lazy val purposeChild: BIP32Node = BIP32Node(PURPOSE, HardenedType.defaultOpt)

View File

@ -1,5 +1,7 @@
package org.bitcoins.core.hd
import org.bitcoins.crypto.StringFactory
/** This is a field that is used in conjunction with BIP44 to indicate what the
* purpose of this [[org.bitcoins.core.crypto.ExtKey ExtKey]] is.
*
@ -25,12 +27,14 @@ case class HDPurpose(constant: Int) extends BIP32Path {
BIP32Node(constant, HardenedType.defaultOpt))
}
object HDPurposes {
object HDPurpose extends StringFactory[HDPurpose] {
final val Legacy = HDPurpose(LegacyHDPath.PURPOSE)
final val Multisig = HDPurpose(MultisigHDPath.PURPOSE)
final val SegWit = HDPurpose(SegWitHDPath.PURPOSE)
final val NestedSegWit = HDPurpose(NestedSegWitHDPath.PURPOSE)
final val default: HDPurpose = SegWit
lazy val singleSigPurposes = Vector(Legacy, SegWit, NestedSegWit)
lazy val all: Vector[HDPurpose] =
@ -44,4 +48,11 @@ object HDPurposes {
s"Cannot construct HDPurpose from un-hardened node: $node")
fromConstant(node.index)
}
override def fromString(string: String): HDPurpose = {
val node = BIP32Node.fromString(string)
fromNode(node).getOrElse {
sys.error(s"Cannot create HDPurpose from string=$string")
}
}
}

View File

@ -11,7 +11,7 @@ object HDUtil {
hdPurpose: HDPurpose,
network: NetworkParameters): ExtKeyPrivVersion = {
import org.bitcoins.core.crypto.ExtKeyVersion._
import org.bitcoins.core.hd.HDPurposes._
import org.bitcoins.core.hd.HDPurpose._
(hdPurpose, network) match {
case (SegWit, MainNet) => SegWitMainNetPriv
@ -35,7 +35,7 @@ object HDUtil {
def getXpubVersion(
hdPurpose: HDPurpose,
network: NetworkParameters): ExtKeyPubVersion = {
import org.bitcoins.core.hd.HDPurposes._
import org.bitcoins.core.hd.HDPurpose._
(hdPurpose, network) match {
case (SegWit, MainNet | SigNet) => ExtKeyPubVersion.SegWitMainNetPub

View File

@ -248,8 +248,7 @@ class DbCommonsColumnMappers(val profile: JdbcProfile) {
MappedColumnType
.base[HDPurpose, Int](
_.constant,
purpose =>
HDPurposes.fromConstant(purpose).getOrElse(HDPurpose(purpose))
purpose => HDPurpose.fromConstant(purpose).getOrElse(HDPurpose(purpose))
)
implicit val bitcoinAddressMapper: BaseColumnType[BitcoinAddress] =

View File

@ -71,7 +71,7 @@ case class DLCOracle()(implicit val conf: DLCOracleAppConfig)
def getRootXpub: ExtPublicKey = extPrivateKey.extPublicKey
private def signingKey: ECPrivateKey = {
val coin = HDCoin(HDPurposes.SegWit, coinType)
val coin = HDCoin(HDPurpose.SegWit, coinType)
val account = HDAccount(coin, 0)
val purpose = coin.purpose
val chain = HDChainType.External

View File

@ -185,7 +185,7 @@ the `-p 9999:9999` port mapping on the docker container to adjust for this.
- `getunusedaddresses` - Returns list of all wallet addresses that have not been used
- `getaccounts` - Returns list of all wallet accounts
- `walletinfo` - Returns meta information about the wallet
- `createnewaccount` - Creates a new wallet account
- `createnewaccount` `purpose` - Creates a new wallet account with the given HD purpose from [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)
- `getaddressinfo` `address` - Returns list of all wallet accounts
- `address` - Address to get information about
- `getnewaddress` - Get a new address

View File

@ -79,7 +79,7 @@ import java.nio.file._
val seedPath = Files.createTempDirectory("key-manager-example").resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
//let's create a native segwit key manager
val purpose = HDPurposes.SegWit
val purpose = HDPurpose.SegWit
//let's choose regtest as our network
val network = RegTest
@ -107,7 +107,7 @@ again after initializing it once. You can use the same `mnemonic` for different
```scala mdoc:to-string
//let's create a nested segwit key manager for mainnet
val mainnetKmParams = KeyManagerParams(seedPath, HDPurposes.SegWit, MainNet)
val mainnetKmParams = KeyManagerParams(seedPath, HDPurpose.SegWit, MainNet)
//we do not need to all `initializeWithMnemonic()` again as we have saved the seed to dis
val mainnetKeyManager = BIP39KeyManager.fromMnemonic(mnemonic, mainnetKmParams, None, Instant.now, false)

View File

@ -23,7 +23,7 @@ import scodec.bits.BitVector
import java.nio.file.Files
class BIP39KeyManagerApiTest extends KeyManagerApiUnitTest {
val purpose = HDPurposes.Legacy
val purpose = HDPurpose.Legacy
// this is taken from 'trezor-addresses.json' which give us test cases that conform with trezor
val mnemonicStr =
@ -339,7 +339,7 @@ class BIP39KeyManagerApiTest extends KeyManagerApiUnitTest {
val seedPath = KeyManagerTestUtil.tmpSeedPath
val aesPasswordOpt = KeyManagerTestUtil.aesPasswordOpt
val kmParams =
keymanagement.KeyManagerParams(seedPath, HDPurposes.SegWit, RegTest)
keymanagement.KeyManagerParams(seedPath, HDPurpose.SegWit, RegTest)
val entropy = MnemonicCode.getEntropy256Bits
val passwordOpt = Some(KeyManagerTestUtil.bip39Password)
val keyManager = withInitializedKeyManager(
@ -376,7 +376,7 @@ class BIP39KeyManagerApiTest extends KeyManagerApiUnitTest {
val seedPath = KeyManagerTestUtil.tmpSeedPath
val aesPasswordOpt = KeyManagerTestUtil.aesPasswordOpt
val kmParams =
keymanagement.KeyManagerParams(seedPath, HDPurposes.SegWit, RegTest)
keymanagement.KeyManagerParams(seedPath, HDPurpose.SegWit, RegTest)
val entropy = MnemonicCode.getEntropy256Bits
val passwordOpt = Some(KeyManagerTestUtil.bip39Password)
val keyManager = withInitializedKeyManager(

View File

@ -17,7 +17,7 @@ import org.bitcoins.core.crypto.ExtKeyVersion.{
SegWitMainNetPriv,
SegWitTestNet3Priv
}
import org.bitcoins.core.hd.{HDCoinType, HDPurpose, HDPurposes}
import org.bitcoins.core.hd.{HDCoinType, HDPurpose}
import org.bitcoins.core.util.HDUtil
import org.bitcoins.testkit.keymanager.KeyManagerApiUnitTest
@ -25,26 +25,26 @@ class HdUtilTest extends KeyManagerApiUnitTest {
it must "get the correct version for a public key" in {
assert(
HDUtil.getXpubVersion(HDPurposes.Legacy, MainNet) == LegacyMainNetPub
HDUtil.getXpubVersion(HDPurpose.Legacy, MainNet) == LegacyMainNetPub
)
assert(
HDUtil.getXpubVersion(HDPurposes.Legacy, TestNet3) == LegacyTestNet3Pub
HDUtil.getXpubVersion(HDPurpose.Legacy, TestNet3) == LegacyTestNet3Pub
)
assert(
HDUtil.getXpubVersion(HDPurposes.SegWit, MainNet) == SegWitMainNetPub
HDUtil.getXpubVersion(HDPurpose.SegWit, MainNet) == SegWitMainNetPub
)
assert(
HDUtil.getXpubVersion(HDPurposes.SegWit, TestNet3) == SegWitTestNet3Pub
HDUtil.getXpubVersion(HDPurpose.SegWit, TestNet3) == SegWitTestNet3Pub
)
assert(
HDUtil.getXpubVersion(
HDPurposes.NestedSegWit,
HDPurpose.NestedSegWit,
MainNet
) == NestedSegWitMainNetPub
)
assert(
HDUtil.getXpubVersion(
HDPurposes.NestedSegWit,
HDPurpose.NestedSegWit,
TestNet3
) == NestedSegWitTestNet3Pub
)
@ -56,26 +56,26 @@ class HdUtilTest extends KeyManagerApiUnitTest {
it must "get the correct version for a private key" in {
assert(
HDUtil.getXprivVersion(HDPurposes.Legacy, MainNet) == LegacyMainNetPriv
HDUtil.getXprivVersion(HDPurpose.Legacy, MainNet) == LegacyMainNetPriv
)
assert(
HDUtil.getXprivVersion(HDPurposes.Legacy, TestNet3) == LegacyTestNet3Priv
HDUtil.getXprivVersion(HDPurpose.Legacy, TestNet3) == LegacyTestNet3Priv
)
assert(
HDUtil.getXprivVersion(HDPurposes.SegWit, MainNet) == SegWitMainNetPriv
HDUtil.getXprivVersion(HDPurpose.SegWit, MainNet) == SegWitMainNetPriv
)
assert(
HDUtil.getXprivVersion(HDPurposes.SegWit, TestNet3) == SegWitTestNet3Priv
HDUtil.getXprivVersion(HDPurpose.SegWit, TestNet3) == SegWitTestNet3Priv
)
assert(
HDUtil.getXprivVersion(
HDPurposes.NestedSegWit,
HDPurpose.NestedSegWit,
MainNet
) == NestedSegWitMainNetPriv
)
assert(
HDUtil.getXprivVersion(
HDPurposes.NestedSegWit,
HDPurpose.NestedSegWit,
TestNet3
) == NestedSegWitTestNet3Priv
)

View File

@ -6,7 +6,7 @@ import org.bitcoins.commons.util.WalletNames
import org.bitcoins.core.api.commons.ArgumentSource
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.crypto.MnemonicCode
import org.bitcoins.core.hd.{HDPurpose, HDPurposes}
import org.bitcoins.core.hd.HDPurpose
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
import org.bitcoins.crypto.{AesPassword, CryptoUtil}
import org.bitcoins.keymanager.bip39.BIP39KeyManager
@ -70,9 +70,9 @@ case class KeyManagerAppConfig(
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
case "legacy" => HDPurpose.Legacy
case "segwit" => HDPurpose.SegWit
case "nested-segwit" => HDPurpose.NestedSegWit
// todo: validate this pre-app startup
case other: String =>
throw new RuntimeException(s"$other is not a valid account type!")

View File

@ -63,7 +63,7 @@ object HDGenerators {
/** Generates a valid HD purpose path */
def hdPurpose: Gen[HDPurpose] =
Gen.oneOf(HDPurposes.Legacy, HDPurposes.NestedSegWit, HDPurposes.SegWit)
Gen.oneOf(HDPurpose.Legacy, HDPurpose.NestedSegWit, HDPurpose.SegWit)
def hdCoin: Gen[HDCoin] =
for {
@ -92,7 +92,7 @@ object HDGenerators {
def legacyHdPath: Gen[LegacyHDPath] =
for {
coinType <- hdCoinType
purpose = HDPurposes.Legacy
purpose = HDPurpose.Legacy
accountIndex <- NumberGenerator.positiveInts
addressIndex <- NumberGenerator.positiveInts
chainType <- hdChainType

View File

@ -7,7 +7,7 @@ import org.bitcoins.core.hd.{
HDChainType,
HDCoin,
HDCoinType,
HDPurposes,
HDPurpose,
LegacyHDPath,
NestedSegWitHDPath,
SegWitHDPath
@ -73,5 +73,5 @@ object WalletTestUtil {
)
val defaultHdAccount: HDAccount =
HDAccount(HDCoin(HDPurposes.SegWit, hdCoinType), 0)
HDAccount(HDCoin(HDPurpose.SegWit, hdCoinType), 0)
}

View File

@ -3,7 +3,7 @@ package org.bitcoins.testkit.keymanager
import java.nio.file.Path
import org.bitcoins.core.config.Networks
import org.bitcoins.core.hd.HDPurposes
import org.bitcoins.core.hd.HDPurpose
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
import org.bitcoins.crypto.AesPassword
import org.bitcoins.keymanager.WalletStorage
@ -27,7 +27,7 @@ object KeyManagerTestUtil {
val seedPath = KeyManagerTestUtil.tmpSeedPath
KeyManagerParams(
seedPath = seedPath,
purpose = Gen.oneOf(HDPurposes.all).sample.get,
purpose = Gen.oneOf(HDPurpose.all).sample.get,
network = Gen.oneOf(Networks.knownNetworks).sample.get
)
}

View File

@ -104,7 +104,7 @@ object WalletTestUtil {
}
val defaultHdAccount: HDAccount =
HDAccount(HDCoin(HDPurposes.SegWit, hdCoinType), 0)
HDAccount(HDCoin(HDPurpose.SegWit, hdCoinType), 0)
def getHdAccount1(walletAppConfig: WalletAppConfig): HDAccount = {
val purpose = walletAppConfig.defaultAccountKind
@ -117,7 +117,7 @@ object WalletTestUtil {
def nestedSegWitAccountDb: AccountDb =
AccountDb(
freshXpub(),
HDAccount(HDCoin(HDPurposes.NestedSegWit, hdCoinType), 0)
HDAccount(HDCoin(HDPurpose.NestedSegWit, hdCoinType), 0)
)
private def randomScriptWitness: ScriptWitness =

View File

@ -1,6 +1,6 @@
package org.bitcoins.wallet
import org.bitcoins.core.hd.{AddressType, HDPurposes}
import org.bitcoins.core.hd.{AddressType, HDPurpose}
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.scalatest.FutureOutcome
@ -20,7 +20,7 @@ class LegacyWalletTest extends BitcoinSWalletTest {
thirdAddr <- wallet.getNewAddress(AddressType.Legacy)
allAddrs <- wallet.listAddresses()
} yield {
assert(account.hdAccount.purpose == HDPurposes.Legacy)
assert(account.hdAccount.purpose == HDPurpose.Legacy)
assert(allAddrs.forall(_.address.isInstanceOf[P2PKHAddress]))
assert(allAddrs.length == 3)
assert(allAddrs.exists(_.address == addr))
@ -34,7 +34,7 @@ class LegacyWalletTest extends BitcoinSWalletTest {
account <- wallet.getDefaultAccountForType(AddressType.SegWit)
addr <- wallet.getNewAddress(AddressType.SegWit)
} yield {
assert(account.hdAccount.purpose == HDPurposes.SegWit)
assert(account.hdAccount.purpose == HDPurpose.SegWit)
assert(addr.isInstanceOf[Bech32Address])
}
}

View File

@ -1,6 +1,6 @@
package org.bitcoins.wallet
import org.bitcoins.core.hd.{AddressType, HDPurposes}
import org.bitcoins.core.hd.{AddressType, HDPurpose}
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.scalatest.FutureOutcome
@ -21,7 +21,7 @@ class SegwitWalletTest extends BitcoinSWalletTest {
thirdAddr <- wallet.getNewAddress(AddressType.SegWit)
allAddrs <- wallet.listAddresses()
} yield {
assert(account.hdAccount.purpose == HDPurposes.SegWit)
assert(account.hdAccount.purpose == HDPurpose.SegWit)
assert(allAddrs.forall(_.address.isInstanceOf[Bech32Address]))
assert(allAddrs.length == 3)
assert(allAddrs.exists(_.address == addr))
@ -35,7 +35,7 @@ class SegwitWalletTest extends BitcoinSWalletTest {
account <- wallet.getDefaultAccountForType(AddressType.Legacy)
addr <- wallet.getNewAddress(AddressType.Legacy)
} yield {
assert(account.hdAccount.purpose == HDPurposes.Legacy)
assert(account.hdAccount.purpose == HDPurpose.Legacy)
assert(addr.isInstanceOf[P2PKHAddress])
}
}

View File

@ -78,9 +78,9 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
override def reads(json: JsValue): JsResult[HDPurpose] =
json.validate[String].map {
case "legacy" => HDPurposes.Legacy
case "segwit" => HDPurposes.SegWit
case "p2sh-segwit" => HDPurposes.NestedSegWit
case "legacy" => HDPurpose.Legacy
case "segwit" => HDPurpose.SegWit
case "p2sh-segwit" => HDPurpose.NestedSegWit
}
}
@ -122,20 +122,20 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
}
lazy val legacyVectors =
vectors.filter(_.pathType == HDPurposes.Legacy)
vectors.filter(_.pathType == HDPurpose.Legacy)
lazy val segwitVectors =
vectors.filter(_.pathType == HDPurposes.SegWit)
vectors.filter(_.pathType == HDPurpose.SegWit)
lazy val nestedVectors =
vectors.filter(_.pathType == HDPurposes.NestedSegWit)
vectors.filter(_.pathType == HDPurpose.NestedSegWit)
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")
case HDPurpose.Legacy => "legacy"
case HDPurpose.SegWit => "segwit"
case HDPurpose.NestedSegWit => "nested-segwit"
case other => fail(s"unexpected purpose: $other")
}
val entropy = mnemonic.toEntropy.toHex
val confStr = s"""bitcoin-s.wallet.defaultAccountType = $purposeStr
@ -236,10 +236,10 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
.walletConf
val testVectors = purpose match {
case HDPurposes.Legacy => legacyVectors
case HDPurposes.SegWit => segwitVectors
case HDPurposes.NestedSegWit => nestedVectors
case other => fail(s"unknown purpose: $other")
case HDPurpose.Legacy => legacyVectors
case HDPurpose.SegWit => segwitVectors
case HDPurpose.NestedSegWit => nestedVectors
case other => fail(s"unknown purpose: $other")
}
val assertionsF: Future[Seq[Assertion]] = for {
@ -306,14 +306,14 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
}
it must "act the same way as Trezor for legacy accounts" in { _ =>
testAccountType(HDPurposes.Legacy)
testAccountType(HDPurpose.Legacy)
}
it must "act the same way as Trezor for segwit accounts" in { _ =>
testAccountType(HDPurposes.SegWit)
testAccountType(HDPurpose.SegWit)
}
it must "act the same way as Trezor for nested segwit accounts" in { _ =>
testAccountType(HDPurposes.NestedSegWit)
testAccountType(HDPurpose.NestedSegWit)
}
}

View File

@ -4,7 +4,7 @@ import java.nio.file.Files
import com.typesafe.config.ConfigFactory
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
import org.bitcoins.core.hd.HDPurposes
import org.bitcoins.core.hd.HDPurpose
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import org.bitcoins.wallet.config.WalletAppConfig
@ -61,8 +61,8 @@ class WalletAppConfigTest extends BitcoinSAsyncTest {
val twiceOverriden = overriden.withOverrides(thirdConf)
assert(overriden.defaultAccountKind == HDPurposes.SegWit)
assert(twiceOverriden.defaultAccountKind == HDPurposes.NestedSegWit)
assert(overriden.defaultAccountKind == HDPurpose.SegWit)
assert(twiceOverriden.defaultAccountKind == HDPurpose.NestedSegWit)
assert(config.datadir == overriden.datadir)
assert(twiceOverriden.datadir == overriden.datadir)

View File

@ -29,7 +29,7 @@ object GetAddresses extends App {
def printerr(x: Any): Unit = System.err.println(x.toString())
val accountInfo = for {
constant <- HDPurposes.singleSigPurposes
constant <- HDPurpose.singleSigPurposes
coin <- List(HDCoinType.Bitcoin /*, HDCoinType.Testnet*/ )
accountIndex <- 0 until 3
} yield {
@ -41,17 +41,17 @@ object GetAddresses extends App {
val pathType =
constant match {
case HDPurposes.Legacy => "legacy"
case HDPurposes.NestedSegWit => "p2sh-segwit"
case HDPurposes.SegWit => "segwit"
case HDPurpose.Legacy => "legacy"
case HDPurpose.NestedSegWit => "p2sh-segwit"
case HDPurpose.SegWit => "segwit"
case other => throw new RuntimeException(s"Unexpected purpose $other")
}
val trezorPathType =
constant match {
case HDPurposes.Legacy => "address"
case HDPurposes.NestedSegWit => "p2shsegwit"
case HDPurposes.SegWit => "segwit"
case HDPurpose.Legacy => "address"
case HDPurpose.NestedSegWit => "p2shsegwit"
case HDPurpose.SegWit => "segwit"
case other => throw new RuntimeException(s"Unexpected purpose $other")
}

View File

@ -1143,7 +1143,7 @@ object Wallet extends WalletLogger {
val createAccountActions: Vector[
DBIOAction[AccountDb, NoStream, Effect.Read with Effect.Write]
] = {
val accounts = HDPurposes.singleSigPurposes.map { purpose =>
val accounts = HDPurpose.singleSigPurposes.map { purpose =>
// we need to create key manager params for each purpose
// and then initialize a key manager to derive the correct xpub
val kmParams = wallet.keyManager.kmParams.copy(purpose = purpose)

View File

@ -98,9 +98,9 @@ case class WalletAppConfig(
lazy val defaultAccountKind: HDPurpose =
config.getString("bitcoin-s.wallet.defaultAccountType") match {
case "legacy" => HDPurposes.Legacy
case "segwit" => HDPurposes.SegWit
case "nested-segwit" => HDPurposes.NestedSegWit
case "legacy" => HDPurpose.Legacy
case "segwit" => HDPurpose.SegWit
case "nested-segwit" => HDPurpose.NestedSegWit
// todo: validate this pre-app startup
case other: String =>
throw new RuntimeException(s"$other is not a valid account type!")
@ -108,9 +108,9 @@ case class WalletAppConfig(
lazy val defaultAddressType: AddressType = {
defaultAccountKind match {
case HDPurposes.Legacy => AddressType.Legacy
case HDPurposes.NestedSegWit => AddressType.NestedSegWit
case HDPurposes.SegWit => AddressType.SegWit
case HDPurpose.Legacy => AddressType.Legacy
case HDPurpose.NestedSegWit => AddressType.NestedSegWit
case HDPurpose.SegWit => AddressType.SegWit
// todo: validate this pre-app startup
case other =>
throw new RuntimeException(s"$other is not a valid account type!")

View File

@ -51,9 +51,9 @@ private[wallet] trait AccountHandling { self: Wallet =>
addressType: AddressType
): Future[AccountDb] = {
val hdCoin = addressType match {
case Legacy => HDCoin(HDPurposes.Legacy, DEFAULT_HD_COIN_TYPE)
case NestedSegWit => HDCoin(HDPurposes.NestedSegWit, DEFAULT_HD_COIN_TYPE)
case SegWit => HDCoin(HDPurposes.SegWit, DEFAULT_HD_COIN_TYPE)
case Legacy => HDCoin(HDPurpose.Legacy, DEFAULT_HD_COIN_TYPE)
case NestedSegWit => HDCoin(HDPurpose.NestedSegWit, DEFAULT_HD_COIN_TYPE)
case SegWit => HDCoin(HDPurpose.SegWit, DEFAULT_HD_COIN_TYPE)
case P2TR =>
throw new UnsupportedOperationException(
s"Taproot not supported in wallet")

View File

@ -368,11 +368,11 @@ private[wallet] trait AddressHandling extends WalletLogger {
val accountIndex = account.hdAccount.index
val path = account.hdAccount.purpose match {
case HDPurposes.Legacy =>
case HDPurpose.Legacy =>
LegacyHDPath(coinType, accountIndex, chainType, addressIndex)
case HDPurposes.NestedSegWit =>
case HDPurpose.NestedSegWit =>
NestedSegWitHDPath(coinType, accountIndex, chainType, addressIndex)
case HDPurposes.SegWit =>
case HDPurpose.SegWit =>
SegWitHDPath(coinType, accountIndex, chainType, addressIndex)
case invalid: HDPurpose =>
@ -396,19 +396,19 @@ private[wallet] trait AddressHandling extends WalletLogger {
}
val addressDb = account.hdAccount.purpose match {
case HDPurposes.SegWit =>
case HDPurpose.SegWit =>
AddressDbHelper.getSegwitAddress(
pubkey,
SegWitHDPath(coinType, accountIndex, chainType, addressIndex),
networkParameters
)
case HDPurposes.NestedSegWit =>
case HDPurpose.NestedSegWit =>
AddressDbHelper.getNestedSegwitAddress(
pubkey,
NestedSegWitHDPath(coinType, accountIndex, chainType, addressIndex),
networkParameters
)
case HDPurposes.Legacy =>
case HDPurpose.Legacy =>
AddressDbHelper.getLegacyAddress(
pubkey,
LegacyHDPath(coinType, accountIndex, chainType, addressIndex),