2024 04 16 descriptor fidelity (#5529)

* Implement unit tests for key expression fidelity to user input for hardened paths

* Create HardenedType, rework BIP32Node to take Option[HardenedType] as a parameter

* Fix docs
This commit is contained in:
Chris Stewart 2024-04-17 18:35:33 -05:00 committed by GitHub
parent d39d89bfed
commit a6d93622f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 184 additions and 117 deletions

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}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path, HardenedType}
import org.bitcoins.testkitcore.gen.{
CryptoGenerators,
HDGenerators,
@ -21,8 +21,8 @@ class BIP32PathTest extends BitcoinSUnitTest {
behavior of "BIP32Child"
it must "fail to make children of out negative integers" in {
forAll(NumberGenerator.negativeInts, Gen.oneOf(true, false)) { (i, bool) =>
assertThrows[IllegalArgumentException](BIP32Node(i, bool))
forAll(NumberGenerator.negativeInts, HDGenerators.hardenedType) { (i, h) =>
assertThrows[IllegalArgumentException](BIP32Node(i, Some(h)))
}
}
@ -104,59 +104,65 @@ class BIP32PathTest extends BitcoinSUnitTest {
it must "parse the paths from the BIP32 test vectors" in {
val expected1 = BIP32Path(
Vector(BIP32Node(0, hardened = true), BIP32Node(1, hardened = false)))
Vector(BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None)))
assert(BIP32Path.fromString("m/0'/1") == expected1)
val expected2 = BIP32Path(
Vector(BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true)))
Vector(BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt)))
assert(BIP32Path.fromString("m/0'/1/2'") == expected2)
val expected3 = BIP32Path(
Vector(BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true),
BIP32Node(2, hardened = false)))
Vector(
BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(2, hardenedOpt = None)
))
assert(BIP32Path.fromString("m/0'/1/2'/2") == expected3)
val expected4 = BIP32Path(
Vector(
BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true),
BIP32Node(2, hardened = false),
BIP32Node(1000000000, hardened = false)
BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(2, hardenedOpt = None),
BIP32Node(1000000000, hardenedOpt = None)
))
assert(BIP32Path.fromString("m/0'/1/2'/2/1000000000") == expected4)
}
it must "parse the paths from the BIP32 test vector from bytes" in {
val expected1 = BIP32Path(
Vector(BIP32Node(0, hardened = true), BIP32Node(1, hardened = false)))
Vector(BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None)))
assert(BIP32Path.fromBytes(hex"0x8000000000000001") == expected1)
val expected2 = BIP32Path(
Vector(BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true)))
Vector(BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt)))
assert(BIP32Path.fromBytes(hex"0x800000000000000180000002") == expected2)
val expected3 = BIP32Path(
Vector(BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true),
BIP32Node(2, hardened = false)))
Vector(
BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(2, hardenedOpt = None)
))
assert(
BIP32Path.fromBytes(hex"0x80000000000000018000000200000002") == expected3)
val expected4 = BIP32Path(
Vector(
BIP32Node(0, hardened = true),
BIP32Node(1, hardened = false),
BIP32Node(2, hardened = true),
BIP32Node(2, hardened = false),
BIP32Node(1000000000, hardened = false)
BIP32Node(0, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(1, hardenedOpt = None),
BIP32Node(2, hardenedOpt = HardenedType.defaultOpt),
BIP32Node(2, hardenedOpt = None),
BIP32Node(1000000000, hardenedOpt = None)
))
assert(BIP32Path
.fromBytes(hex"0x800000000000000180000002000000023B9ACA00") == expected4)
@ -169,13 +175,6 @@ class BIP32PathTest extends BitcoinSUnitTest {
}
}
it must "have fromBytes and bytes symmetry" in {
forAll(HDGenerators.bip32Path) { path =>
val bytes = path.bytes
assert(path == BIP32Path.fromBytes(bytes))
}
}
it must "do path diffing" in {
{
val first = BIP32Path.fromString("m/44'/1'")

View File

@ -40,7 +40,7 @@ class HDAccountTest extends BitcoinSUnitTest {
}
it must "succeed if we add an arbitrary element onto the end of the path" in {
val extraNode = defaultPath.:+(BIP32Node(0, true))
val extraNode = defaultPath.:+(BIP32Node(0, HardenedType.defaultOpt))
val isSame = HDAccount.isSameAccount(extraNode, defaultAcct)

View File

@ -129,7 +129,7 @@ class HDPathTest extends BitcoinSUnitTest {
forAll(HDGenerators.hdPathWithConstructor) { case (hd, hdApply) =>
val nonHardenedCoinChildren = hd.path.zipWithIndex.map {
case (child, index) =>
if (index == LegacyHDPath.COIN_INDEX) child.copy(hardened = false)
if (index == LegacyHDPath.COIN_INDEX) child.copy(hardenedOpt = None)
else child
}
@ -144,7 +144,7 @@ class HDPathTest extends BitcoinSUnitTest {
val nonHardenedAccountChildren = hd.path.zipWithIndex.map {
case (child, index) =>
if (index == LegacyHDPath.ACCOUNT_INDEX)
child.copy(hardened = false)
child.copy(hardenedOpt = None)
else child
}
val badAccountAttempt = hdApply(nonHardenedAccountChildren)
@ -157,7 +157,8 @@ class HDPathTest extends BitcoinSUnitTest {
val hardenedChainChildren = hd.path.zipWithIndex.map {
case (child, index) =>
if (index == LegacyHDPath.CHAIN_INDEX) child.copy(hardened = true)
if (index == LegacyHDPath.CHAIN_INDEX)
child.copy(hardenedOpt = HardenedType.defaultOpt)
else child
}
val badChainAttempt =
@ -171,7 +172,8 @@ class HDPathTest extends BitcoinSUnitTest {
val hardenedAddressChildren = hd.path.zipWithIndex.map {
case (child, index) =>
if (index == LegacyHDPath.ADDRESS_INDEX) child.copy(hardened = true)
if (index == LegacyHDPath.ADDRESS_INDEX)
child.copy(hardenedOpt = HardenedType.defaultOpt)
else child
}
val badAddrAttempt =

View File

@ -7,32 +7,43 @@ class DescriptorChecksumTest extends BitcoinSUnitTest {
behavior of "DescriptorChecksumTest"
val expression =
RawScriptExpression(NonStandardScriptPubKey.fromAsmHex("deadbeef"))
val descriptor =
RawDescriptor(
RawScriptExpression(NonStandardScriptPubKey.fromAsmHex("deadbeef")),
None)
it must "calculate correct checksums from BIP380 examples" in {
val str0 = "raw(deadbeef)#89f8spxm"
val split0 = str0.split("#")
val (payload, checksum) = (split0(0), split0(1))
assert(Descriptor.createChecksum(payload) == checksum)
assert(Descriptor.isValidChecksum(expression, Some(checksum)))
assert(Descriptor.isValidChecksum(descriptor, Some(checksum)))
//expression with nochecksum should be valid
assert(Descriptor.isValidChecksum(expression, None))
assert(Descriptor.isValidChecksum(descriptor, None))
// val descriptor1 =
// Descriptor.fromString(
// "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)")
// val checksum1 = "cjjspncu"
// assert(Descriptor.createChecksum(descriptor1) == checksum1)
// assert(Descriptor.isValidChecksum(descriptor1, Some(checksum1)))
}
it must "fail when a bad checksum is given" in {
//Missing checksum
assert(!Descriptor.isValidChecksum(expression, Some("#")))
assert(!Descriptor.isValidChecksum(descriptor, Some("#")))
//Too long checksum (9 chars)
assert(!Descriptor.isValidChecksum(expression, Some("89f8spxmx")))
assert(!Descriptor.isValidChecksum(descriptor, Some("89f8spxmx")))
//Too short checksum (7 chars)
assert(!Descriptor.isValidChecksum(expression, Some("89f8spx")))
assert(!Descriptor.isValidChecksum(descriptor, Some("89f8spx")))
//Error in payload
val bad =
RawScriptExpression(NonStandardScriptPubKey.fromAsmHex("deedbeef"))
RawDescriptor(
RawScriptExpression(NonStandardScriptPubKey.fromAsmHex("deedbeef")),
None)
assert(!Descriptor.isValidChecksum(bad, Some("89f8spxm")))
//Error in checksum
assert(!Descriptor.isValidChecksum(expression, Some("#9f8spxm")))
assert(!Descriptor.isValidChecksum(descriptor, Some("#9f8spxm")))
}
}

View File

@ -452,6 +452,14 @@ class DescriptorTest extends BitcoinSUnitTest {
runFailTest(str4)
}
it must "have fidelity with the type of hardened derivation used as input" in {
//note using h instead of ' for hardened derivation path
val str =
"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)"
val desc = Descriptor.fromString(str)
assert(desc.toString == str)
}
def runTest(descriptor: String, expectedSPK: String): Assertion = {
val desc = ScriptDescriptor.fromString(descriptor)
assert(desc.toString == descriptor)

View File

@ -13,8 +13,8 @@ class KeyExpressionTest extends BitcoinSUnitTest {
val str2 = "[deadbeef/0'/0h/0']"
val keyOrigin = KeyOriginExpression.fromString(str0)
assert(str0 == keyOrigin.toString)
assert(keyOrigin == KeyOriginExpression.fromString(str1))
keyOrigin == KeyOriginExpression.fromString(str2)
assert(KeyOriginExpression.fromString(str1).toString == str1)
assert(KeyOriginExpression.fromString(str2).toString == str2)
}
it must "parse valid private key expressions from BIP380" in {

View File

@ -80,13 +80,14 @@ abstract class BIP32Path extends SeqWrapper[BIP32Node] {
}
}
override def toString: String =
override def toString: String = {
path
.map { case BIP32Node(index, hardened) =>
val isHardened = if (hardened) "'" else ""
.map { case BIP32Node(index, hardenedOpt) =>
val isHardened = hardenedOpt.map(_.toString).getOrElse("")
index.toString + isHardened
}
.fold("m")((accum, curr) => accum + "/" + curr)
}
def bytes: ByteVector = path.foldLeft(ByteVector.empty)(_ ++ _.toUInt32.bytes)
@ -139,13 +140,13 @@ object BIP32Path extends Factory[BIP32Path] with StringFactory[BIP32Path] {
"""The first element in a BIP32 path string must be "m"""")
val path = rest.map { str =>
val (index: String, hardened: Boolean) =
if (str.endsWith("'") || str.endsWith("h")) {
(str.dropRight(1), true)
} else {
(str, false)
val (index: String, hardenedOpt: Option[HardenedType]) = {
HardenedType.fromStringOpt(str.last.toString) match {
case Some(h) => (str.dropRight(1), Some(h))
case None => (str, None)
}
BIP32Node(index.toInt, hardened)
}
BIP32Node(index.toInt, hardenedOpt)
}
BIP32PathImpl(path)
@ -173,7 +174,7 @@ object BIP32Path extends Factory[BIP32Path] with StringFactory[BIP32Path] {
if (littleEndian) UInt32.fromBytesLE(part) else UInt32.fromBytes(part)
val hardened = uInt32 >= ExtKey.hardenedIdx
val index = if (hardened) uInt32 - ExtKey.hardenedIdx else uInt32
BIP32Node(index.toInt, hardened)
BIP32Node(index.toInt, if (hardened) Some(HardenedType.default) else None)
}
BIP32Path(path)
@ -187,13 +188,43 @@ object BIP32Path extends Factory[BIP32Path] with StringFactory[BIP32Path] {
}
case class BIP32Node(index: Int, hardened: Boolean) {
case class BIP32Node(index: Int, hardenedOpt: Option[HardenedType]) {
require(index >= 0, s"BIP32 node index must be positive! Got $index")
def hardened: Boolean = hardenedOpt.isDefined
/** Converts this node to a BIP32 notation
* unsigned 32 bit integer
*/
def toUInt32: UInt32 =
if (hardened) ExtKey.hardenedIdx + UInt32(index.toLong)
if (hardenedOpt.isDefined) ExtKey.hardenedIdx + UInt32(index.toLong)
else UInt32(index)
}
sealed abstract class HardenedType
object HardenedType extends StringFactory[HardenedType] {
case object Tick extends HardenedType {
override def toString: String = {
"'"
}
}
case object h extends HardenedType {
override def toString(): String = "h"
}
val all: Set[HardenedType] = Set(Tick, h)
override def fromString(string: String): HardenedType = {
all
.find(_.toString == string)
.getOrElse(sys.error(s"Cannot find HardenedType for string=$string"))
}
val default: HardenedType = Tick
val defaultOpt: Option[HardenedType] = Some(default)
}

View File

@ -16,7 +16,7 @@ case class HDAccount(
require(index >= 0, s"Account index ($index) must be positive!")
override val path: Vector[BIP32Node] = {
coin.path :+ BIP32Node(index, hardened = true)
coin.path :+ BIP32Node(index, hardenedOpt = HardenedType.defaultOpt)
}
def purpose: HDPurpose = coin.purpose

View File

@ -8,7 +8,7 @@ sealed abstract class HDAddress extends BIP32Path {
require(index >= 0, s"Address index ($index) must be positive!")
override val path: Vector[BIP32Node] = {
chain.path :+ BIP32Node(index, hardened = false)
chain.path :+ BIP32Node(index, hardenedOpt = None)
}
def purpose: HDPurpose

View File

@ -7,7 +7,7 @@ package org.bitcoins.core.hd
sealed abstract class HDChain extends BIP32Path {
override val path: Vector[BIP32Node] = {
account.path :+ BIP32Node(toInt, hardened = false)
account.path :+ BIP32Node(toInt, hardenedOpt = None)
}
def purpose: HDPurpose

View File

@ -7,7 +7,7 @@ package org.bitcoins.core.hd
case class HDCoin(purpose: HDPurpose, coinType: HDCoinType) extends BIP32Path {
override def path: Vector[BIP32Node] =
purpose.path :+ BIP32Node(coinType.toInt, hardened = true)
purpose.path :+ BIP32Node(coinType.toInt, HardenedType.defaultOpt)
def toAccount(index: Int): HDAccount = HDAccount(this, index)
}

View File

@ -41,15 +41,16 @@ private[hd] trait HDPathFactory[PathType <: BIP32Path]
val maybePurpose = children.head
val purpose: HDPurpose = maybePurpose match {
case BIP32Node(_, false) =>
case BIP32Node(_, None) =>
throw new IllegalArgumentException(
"The first child in a HD path must be hardened")
case BIP32Node(HDPurposes.Legacy.constant, true) => HDPurposes.Legacy
case BIP32Node(HDPurposes.SegWit.constant, true) => HDPurposes.SegWit
case BIP32Node(HDPurposes.NestedSegWit.constant, true) =>
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, true) => HDPurposes.Multisig
case BIP32Node(unknown, true) =>
case BIP32Node(HDPurposes.Multisig.constant, Some(_)) =>
HDPurposes.Multisig
case BIP32Node(unknown, Some(_)) =>
throw new IllegalArgumentException(
s"Purpose constant ($unknown) is not a known purpose constant")
}
@ -103,7 +104,7 @@ private[hd] trait HDPathFactory[PathType <: BIP32Path]
protected lazy val hdPurpose: HDPurpose =
HDPurposes.fromConstant(PURPOSE).get // todo
lazy val purposeChild: BIP32Node = BIP32Node(PURPOSE, hardened = true)
lazy val purposeChild: BIP32Node = BIP32Node(PURPOSE, HardenedType.defaultOpt)
/** The index of the coin segement of a BIP44 path
*/

View File

@ -18,7 +18,7 @@ package org.bitcoins.core.hd
case class HDPurpose(constant: Int) extends BIP32Path {
override val path: Vector[BIP32Node] = Vector(
BIP32Node(constant, hardened = true))
BIP32Node(constant, HardenedType.defaultOpt))
}
object HDPurposes {

View File

@ -141,7 +141,9 @@ sealed abstract class DescriptorFactory[
//now check for a valid checksum
val checksumOpt =
if (checksum.nonEmpty) Some(checksum.tail) else None //drop '#'
val isValidChecksum = Descriptor.isValidChecksum(expression, checksumOpt)
val isValidChecksum = Descriptor.isValidChecksum(
descriptor = createDescriptor(expression, None),
checksumOpt = checksumOpt)
if (isValidChecksum) {
createDescriptor(expression, checksumOpt)
} else {
@ -468,13 +470,17 @@ object Descriptor extends StringFactory[Descriptor] {
builder.result()
}
def createChecksum(descriptor: Descriptor): String = {
createChecksum(descriptor.toString)
}
def isValidChecksum(
expression: DescriptorExpression,
descriptor: Descriptor,
checksumOpt: Option[String]): Boolean = {
checksumOpt match {
case None => true //trivially true if we have no checksum
case Some(checksum) =>
val t = Try(createChecksum(expression.toString))
val t = Try(createChecksum(descriptor.toString))
if (t.isFailure) false
else t.get == checksum
}

View File

@ -7,7 +7,7 @@ import org.bitcoins.core.crypto.{
ExtPrivateKey,
ExtPublicKey
}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path}
import org.bitcoins.core.hd.{BIP32Node, BIP32Path, HardenedType}
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.script.ScriptType
import org.bitcoins.crypto._
@ -175,20 +175,25 @@ sealed abstract class ExtECPublicKeyExpression
def pathOpt: Option[BIP32Path]
def childrenHardenedOpt: Option[Boolean]
/** Outer Option represents if we use this key or derive children
* Inner option represents whether child keys are hardened or not
* if they are hardedned, return the specifi [[HardenedType]]
*/
def childrenHardenedOpt: Option[Option[HardenedType]]
def deriveChild(idx: Int): BaseECKey
override def toString(): String = {
val hardenedStr: String = childrenHardenedOpt match {
case Some(Some(h)) => s"/*${h.toString}"
case Some(None) => "/*"
case None => ""
}
originOpt.map(_.toString).getOrElse("") +
ExtKey.toString(extKey) +
pathOpt.map(_.toString.drop(1)).getOrElse("") +
childrenHardenedOpt
.map {
case true => "/*'"
case false => "/*"
}
.getOrElse("")
hardenedStr
}
}
@ -204,7 +209,7 @@ sealed abstract class ExtXOnlyPublicKeyExpression
def pathOpt: Option[BIP32Path] = ecPublicKeyExpression.pathOpt
def childrenHardenedOpt: Option[Boolean] =
def childrenHardenedOpt: Option[Option[HardenedType]] =
ecPublicKeyExpression.childrenHardenedOpt
def deriveChild(idx: Int): BaseECKey = ecPublicKeyExpression.deriveChild(idx)
@ -221,7 +226,7 @@ case class XprvECPublicKeyExpression(
override val extKey: ExtPrivateKey,
originOpt: Option[KeyOriginExpression],
pathOpt: Option[BIP32Path],
childrenHardenedOpt: Option[Boolean])
childrenHardenedOpt: Option[Option[HardenedType]])
extends ExtECPublicKeyExpression
with ECPublicKeyExpression {
@ -242,7 +247,7 @@ case class XprvECPublicKeyExpression(
childrenHardenedOpt.isDefined,
s"Cannot derive child keys from descriptor that does not allow children, got=${toString}")
val node =
BIP32Node(index = idx, hardened = childrenHardenedOpt.getOrElse(false))
BIP32Node(index = idx, hardenedOpt = childrenHardenedOpt.get)
val fullPath: BIP32Path = pathOpt match {
case Some(p) => BIP32Path(p.path.appended(node))
case None => BIP32Path(node)
@ -270,7 +275,7 @@ case class XpubECPublicKeyExpression(
override val extKey: ExtPublicKey,
originOpt: Option[KeyOriginExpression],
pathOpt: Option[BIP32Path],
childrenHardenedOpt: Option[Boolean])
childrenHardenedOpt: Option[Option[HardenedType]])
extends ExtECPublicKeyExpression
with ECPublicKeyExpression {
@ -290,7 +295,7 @@ case class XpubECPublicKeyExpression(
require(
childrenHardenedOpt.isDefined,
s"Cannot derive child keys from descriptor that does not allow children, got=${toString}")
val node = BIP32Node(index = idx, hardened = childrenHardenedOpt.get)
val node = BIP32Node(index = idx, hardenedOpt = childrenHardenedOpt.get)
val fullPath: BIP32Path = pathOpt match {
case Some(p) => BIP32Path(p.path.appended(node))
case None => BIP32Path(node)

View File

@ -1,7 +1,7 @@
package org.bitcoins.core.protocol.script.descriptor
import org.bitcoins.core.crypto.{ECPrivateKeyUtil, ExtKey, ExtPublicKey}
import org.bitcoins.core.hd.BIP32Path
import org.bitcoins.core.hd.{BIP32Path, HardenedType}
import org.bitcoins.core.protocol.script.{
MultiSignatureScriptPubKey,
RawScriptPubKey
@ -11,10 +11,6 @@ import org.bitcoins.crypto._
case class DescriptorIterator(descriptor: String) {
private var index: Int = 0
private val hardenedChars: Vector[Char] = {
Vector('\'', 'h', 'H')
}
def current: String = {
descriptor.drop(index)
}
@ -46,15 +42,20 @@ case class DescriptorIterator(descriptor: String) {
}
}
def takeChildrenHardenedOpt(): Option[Boolean] = {
def takeChildrenHardenedOpt(): Option[Option[HardenedType]] = {
if (current.nonEmpty && current.charAt(0) == '*') {
skip(1)
if (current.nonEmpty && hardenedChars.exists(_ == current.charAt(0))) {
val hardenedOpt = HardenedType.fromStringOpt(current.take(1))
// if (current.nonEmpty && hardenedChars.exists(_ == current.charAt(0))) {
// skip(1)
// Some(true)
// } else {
// Some(false)
// }
if (hardenedOpt.isDefined) {
skip(1)
Some(true)
} else {
Some(false)
}
Some(hardenedOpt)
} else {
None
}

View File

@ -749,8 +749,8 @@ abstract class DLCWallet
account: AccountDb,
keyIndex: Int): AdaptorSign = {
val bip32Path = BIP32Path(
account.hdAccount.path ++ Vector(BIP32Node(0, hardened = false),
BIP32Node(keyIndex, hardened = false)))
account.hdAccount.path ++ Vector(BIP32Node(0, hardenedOpt = None),
BIP32Node(keyIndex, hardenedOpt = None)))
val privKeyPath = HDPath.fromString(bip32Path.toString)
keyManager.toSign(privKeyPath)
}

View File

@ -572,8 +572,8 @@ case class DLCDataManagement(dlcWalletDAOs: DLCWalletDAOs)(implicit
val bip32Path = BIP32Path(
dlcDb.account.path ++ Vector(
BIP32Node(dlcDb.changeIndex.index, hardened = false),
BIP32Node(dlcDb.keyIndex, hardened = false)))
BIP32Node(dlcDb.changeIndex.index, hardenedOpt = None),
BIP32Node(dlcDb.keyIndex, hardenedOpt = None)))
val privKeyPath = HDPath.fromString(bip32Path.toString)
val fundingPrivKey = keyManager.toSign(privKeyPath)

View File

@ -51,7 +51,7 @@ val extPrivKey = ExtPrivateKey(ExtKeyVersion.SegWitMainNetPriv)
extPrivKey.sign(DoubleSha256Digest.empty.bytes)
val path = BIP32Path(Vector(BIP32Node(0,false)))
val path = BIP32Path(Vector(BIP32Node(0,HardenedType.defaultOpt)))
extPrivKey.sign(DoubleSha256Digest.empty.bytes,path)
```

View File

@ -8,6 +8,8 @@ import scala.util.Try
*/
object HDGenerators {
def hardenedType: Gen[HardenedType] = Gen.oneOf(HardenedType.all)
/** Generates a BIP 32 path segment
*/
def bip32Child: Gen[BIP32Node] = Gen.oneOf(softBip32Child, hardBip32Child)
@ -17,14 +19,15 @@ object HDGenerators {
def softBip32Child: Gen[BIP32Node] =
for {
index <- NumberGenerator.positiveInts
} yield BIP32Node(index, hardened = false)
} yield BIP32Node(index, hardenedOpt = None)
/** Generates a hardened BIP 32 path segment
*/
def hardBip32Child: Gen[BIP32Node] =
for {
soft <- softBip32Child
} yield soft.copy(hardened = true)
hardened <- hardenedType
} yield soft.copy(hardenedOpt = Some(hardened))
/** Generates a BIP32 path
*/

View File

@ -37,9 +37,9 @@ object GetAddresses extends App {
accountIndex <- 0 until 3
} yield {
val accountPath = BIP32Path(
BIP32Node(constant.constant, hardened = true),
BIP32Node(coin.toInt, hardened = true),
BIP32Node(accountIndex, hardened = true)
BIP32Node(constant.constant, HardenedType.defaultOpt),
BIP32Node(coin.toInt, HardenedType.defaultOpt),
BIP32Node(accountIndex, HardenedType.defaultOpt)
)
val pathType =
@ -68,11 +68,11 @@ object GetAddresses extends App {
addressIndex <- 0 until 3
} yield {
val path = BIP32Path(
BIP32Node(constant.constant, hardened = true),
BIP32Node(coin.toInt, hardened = true),
BIP32Node(accountIndex, hardened = true),
BIP32Node(chainType.index, hardened = false),
BIP32Node(addressIndex, hardened = false)
BIP32Node(constant.constant, HardenedType.defaultOpt),
BIP32Node(coin.toInt, HardenedType.defaultOpt),
BIP32Node(accountIndex, HardenedType.defaultOpt),
BIP32Node(chainType.index, HardenedType.defaultOpt),
BIP32Node(addressIndex, None)
)
val addressCmd = s"trezorctl get-address -n $path -t $trezorPathType"