Skip to content

Commit 440414c

Browse files
authored
Update bitcoin-kmp and secp256k1-kmp (#104)
* Update bitcoin-kmp to 0.29.0 (taproot tweak refactoring) and secp256k1-kmp * Publish snapshots to https://central.sonatype.com/repository/maven-snapshots * Set version to 0.46 * Remove sonatype snapshot repository We can always add it back for PR that need to test against snapshot dependencies.
1 parent aa0fee4 commit 440414c

File tree

6 files changed

+48
-66
lines changed

6 files changed

+48
-66
lines changed

pom.xml

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
<groupId>fr.acinq</groupId>
44
<artifactId>bitcoin-lib_2.13</artifactId>
55
<packaging>jar</packaging>
6-
<version>0.45.2-SNAPSHOT</version>
6+
<version>0.46</version>
77
<description>Simple Scala Bitcoin library</description>
88
<url>https://github.com/ACINQ/bitcoin-lib</url>
99
<name>bitcoin-lib</name>
1010

11+
<distributionManagement>
12+
<snapshotRepository>
13+
<id>central-snapshots</id>
14+
<url>https://central.sonatype.com/repository/maven-snapshots</url>
15+
</snapshotRepository>
16+
</distributionManagement>
17+
1118
<properties>
1219
<project.build.outputTimestamp>2020-01-01T00:00:00Z</project.build.outputTimestamp>
1320
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -40,20 +47,6 @@
4047
</developer>
4148
</developers>
4249

43-
<repositories>
44-
<repository>
45-
<id>central-snapshots</id>
46-
<name>Sonatype Central Portal (snapshots)</name>
47-
<url>https://central.sonatype.com/repository/maven-snapshots</url>
48-
<releases>
49-
<enabled>false</enabled>
50-
</releases>
51-
<snapshots>
52-
<enabled>true</enabled>
53-
</snapshots>
54-
</repository>
55-
</repositories>
56-
5750
<build>
5851
<plugins>
5952
<plugin>
@@ -153,12 +146,12 @@
153146
<dependency>
154147
<groupId>fr.acinq.bitcoin</groupId>
155148
<artifactId>bitcoin-kmp-jvm</artifactId>
156-
<version>0.28.1</version>
149+
<version>0.29.0</version>
157150
</dependency>
158151
<dependency>
159152
<groupId>fr.acinq.secp256k1</groupId>
160153
<artifactId>secp256k1-kmp-jni-jvm</artifactId>
161-
<version>0.21.0</version>
154+
<version>0.22.0</version>
162155
</dependency>
163156
<dependency>
164157
<groupId>org.scodec</groupId>

src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@ import scodec.bits.ByteVector
77
object Crypto {
88

99
// @formatter:off
10-
/** Specify how private keys are tweaked when creating Schnorr signatures. */
11-
sealed trait SchnorrTweak
12-
object SchnorrTweak {
13-
/** The private key is directly used, without any tweaks. */
14-
case object NoTweak extends SchnorrTweak
15-
}
16-
17-
sealed trait TaprootTweak extends SchnorrTweak
10+
sealed trait TaprootTweak
1811
object TaprootTweak {
1912
/** The private key is tweaked with H_TapTweak(public key) (this is used for key path spending when there is no script tree). */
20-
case object NoScriptTweak extends TaprootTweak
13+
case object KeyPathTweak extends TaprootTweak
2114
/** The private key is tweaked with H_TapTweak(public key || merkle_root) (this is used for key path spending when a script tree exists). */
22-
case class ScriptTweak(merkleRoot: ByteVector32) extends TaprootTweak
23-
object ScriptTweak {
24-
def apply(scriptTree: ScriptTree): ScriptTweak = ScriptTweak(scriptTree.hash())
15+
case class ScriptPathTweak(merkleRoot: ByteVector32) extends TaprootTweak
16+
object ScriptPathTweak {
17+
def apply(scriptTree: ScriptTree): ScriptPathTweak = ScriptPathTweak(scriptTree.hash())
2518
}
2619
}
2720
// @formatter:on
@@ -159,10 +152,10 @@ object Crypto {
159152
}
160153

161154
/** Tweak this key with the merkle root of the given script tree. */
162-
def outputKey(scriptTree: ScriptTree): (XonlyPublicKey, Boolean) = outputKey(TaprootTweak.ScriptTweak(scriptTree))
155+
def outputKey(scriptTree: ScriptTree): (XonlyPublicKey, Boolean) = outputKey(TaprootTweak.ScriptPathTweak(scriptTree))
163156

164157
/** Tweak this key with the merkle root provided. */
165-
def outputKey(merkleRoot: ByteVector32): (XonlyPublicKey, Boolean) = outputKey(TaprootTweak.ScriptTweak(merkleRoot))
158+
def outputKey(merkleRoot: ByteVector32): (XonlyPublicKey, Boolean) = outputKey(TaprootTweak.ScriptPathTweak(merkleRoot))
166159

167160
/**
168161
* add a public key to this x-only key
@@ -295,8 +288,8 @@ object Crypto {
295288
* the key (there is an extra "1" appended to the key)
296289
* @return a signature in compact format (64 bytes)
297290
*/
298-
def signSchnorr(data: ByteVector32, privateKey: PrivateKey, schnorrTweak: SchnorrTweak = SchnorrTweak.NoTweak, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
299-
bitcoin.Crypto.signSchnorr(data, privateKey, scala2kmp(schnorrTweak), auxrand32.map(scala2kmp).orNull)
291+
def signSchnorr(data: ByteVector32, privateKey: PrivateKey, schnorrTweak_opt: Option[TaprootTweak] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
292+
bitcoin.Crypto.signSchnorr(data, privateKey, schnorrTweak_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
300293
}
301294

302295
/**

src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,13 @@ object KotlinUtils {
6565
}
6666

6767
implicit def kmp2scala(input: bitcoin.Crypto.TaprootTweak): TaprootTweak = input match {
68-
case bitcoin.Crypto.TaprootTweak.NoScriptTweak.INSTANCE => TaprootTweak.NoScriptTweak
69-
case tweak: bitcoin.Crypto.TaprootTweak.ScriptTweak => TaprootTweak.ScriptTweak(kmp2scala(tweak.getMerkleRoot))
70-
case _ => ??? // this cannot happen, but the compiler cannot know that there aren't other cases
68+
case bitcoin.Crypto.TaprootTweak.KeyPathTweak.INSTANCE => TaprootTweak.KeyPathTweak
69+
case tweak: bitcoin.Crypto.TaprootTweak.ScriptPathTweak => TaprootTweak.ScriptPathTweak(kmp2scala(tweak.getMerkleRoot))
7170
}
7271

7372
implicit def scala2kmp(input: TaprootTweak): bitcoin.Crypto.TaprootTweak = input match {
74-
case TaprootTweak.NoScriptTweak => bitcoin.Crypto.TaprootTweak.NoScriptTweak.INSTANCE
75-
case tweak: TaprootTweak.ScriptTweak => new bitcoin.Crypto.TaprootTweak.ScriptTweak(scala2kmp(tweak.merkleRoot))
76-
}
77-
78-
implicit def kmp2scala(input: bitcoin.Crypto.SchnorrTweak): SchnorrTweak = input match {
79-
case bitcoin.Crypto.SchnorrTweak.NoTweak.INSTANCE => SchnorrTweak.NoTweak
80-
case tweak: bitcoin.Crypto.TaprootTweak => kmp2scala(tweak)
81-
case _ => ??? // this cannot happen, but the compiler cannot know that there aren't other cases
82-
}
83-
84-
implicit def scala2kmp(input: SchnorrTweak): bitcoin.Crypto.SchnorrTweak = input match {
85-
case SchnorrTweak.NoTweak => bitcoin.Crypto.SchnorrTweak.NoTweak.INSTANCE
86-
case tweak: TaprootTweak => scala2kmp(tweak)
73+
case TaprootTweak.KeyPathTweak => bitcoin.Crypto.TaprootTweak.KeyPathTweak.INSTANCE
74+
case tweak: TaprootTweak.ScriptPathTweak => new bitcoin.Crypto.TaprootTweak.ScriptPathTweak(scala2kmp(tweak.merkleRoot))
8775
}
8876

8977
implicit def kmp2scala(input: bitcoin.TxIn): TxIn = TxIn(input.outPoint, input.signatureScript, input.sequence, input.witness)

src/main/scala/fr/acinq/bitcoin/scalacompat/Script.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,22 @@ object Script {
166166
def witnessPay2wpkh(pubKey: PublicKey, sig: ByteVector): ScriptWitness = bitcoin.Script.witnessPay2wpkh(pubKey, sig)
167167

168168
/**
169-
* @param outputKey public key exposed by the taproot script (tweaked based on the tapscripts).
170-
* @return a pay-to-taproot script.
169+
* @param internalKey internal public key that will be tweaked with the [scripts] provided.
170+
* @param scriptsRoot spending scripts that can be used instead of key-path spending.
171171
*/
172-
def pay2tr(outputKey: XonlyPublicKey): Seq[ScriptElt] = bitcoin.Script.pay2tr(outputKey.pub).asScala.map(kmp2scala).toList
172+
def pay2tr(internalKey: XonlyPublicKey, scriptsRoot: ByteVector32): Seq[ScriptElt] = bitcoin.Script.pay2tr(internalKey.pub, scriptsRoot).asScala.map(kmp2scala).toList
173173

174174
/**
175175
* @param internalKey internal public key that will be tweaked with the [scripts] provided.
176-
* @param scripts_opt optional spending scripts that can be used instead of key-path spending.
176+
* @param scripts spending scripts that can be used instead of key-path spending.
177+
*/
178+
def pay2tr(internalKey: XonlyPublicKey, scripts: ScriptTree): Seq[ScriptElt] = pay2tr(internalKey, scripts.hash())
179+
180+
/**
181+
* @param internalKey internal public key that will be tweaked with the provided [taprootTweak].
182+
* @param taprootTweak tweak to apply to [internalKey].
177183
*/
178-
def pay2tr(internalKey: XonlyPublicKey, scripts_opt: Option[ScriptTree]): Seq[ScriptElt] = bitcoin.Script.pay2tr(internalKey.pub, scripts_opt.map(scala2kmp).orNull).asScala.map(kmp2scala).toList
184+
def pay2tr(internalKey: XonlyPublicKey, taprootTweak: Crypto.TaprootTweak): Seq[ScriptElt] = bitcoin.Script.pay2tr(internalKey.pub, taprootTweak).asScala.map(kmp2scala).toList
179185

180186
def isPay2tr(script: Seq[ScriptElt]): Boolean = bitcoin.Script.isPay2tr(script.map(scala2kmp).asJava)
181187

src/test/scala/fr/acinq/bitcoin/scalacompat/Musig2Spec.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package fr.acinq.bitcoin.scalacompat
22

33
import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey
4+
import fr.acinq.bitcoin.scalacompat.Crypto.TaprootTweak.KeyPathTweak
45
import fr.acinq.bitcoin.{ScriptFlags, SigHash}
56
import org.scalatest.FunSuite
67
import scodec.bits.{ByteVector, HexStringSyntax}
@@ -19,7 +20,7 @@ class Musig2Spec extends FunSuite {
1920
val internalPubKey = Musig2.aggregateKeys(Seq(alicePubKey, bobPubKey))
2021

2122
// This tx sends to a taproot script that doesn't contain any script path.
22-
val tx = Transaction(2, Nil, Seq(TxOut(10_000 sat, Script.pay2tr(internalPubKey, scripts_opt = None))), 0)
23+
val tx = Transaction(2, Nil, Seq(TxOut(10_000 sat, Script.pay2tr(internalPubKey, KeyPathTweak))), 0)
2324
// This tx spends the previous tx with Alice and Bob's signatures.
2425
val spendingTx = Transaction(2, Seq(TxIn(OutPoint(tx, 0), ByteVector.empty, 0)), Seq(TxOut(10_000 sat, Script.pay2wpkh(alicePubKey))), 0)
2526

@@ -59,7 +60,7 @@ class Musig2Spec extends FunSuite {
5960
// The internal pubkey is the musig2 aggregation of the user's and server's public keys: it does not depend upon the user's refund's key.
6061
val aggregatedKey = Musig2.aggregateKeys(Seq(userPublicKey, serverPublicKey))
6162
// It is tweaked with the script's merkle root to get the pubkey that will be exposed.
62-
val pubkeyScript = Script.pay2tr(aggregatedKey, Some(scriptTree))
63+
val pubkeyScript = Script.pay2tr(aggregatedKey, scriptTree)
6364

6465
val swapInTx = Transaction(
6566
version = 2,

src/test/scala/fr/acinq/bitcoin/scalacompat/TaprootSpec.scala

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package fr.acinq.bitcoin.scalacompat
22

3+
import fr.acinq.bitcoin.scalacompat.Crypto.TaprootTweak.KeyPathTweak
34
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, TaprootTweak}
45
import fr.acinq.bitcoin.scalacompat.KotlinUtils._
56
import fr.acinq.bitcoin.{Bech32, ScriptFlags, SigHash, SigVersion}
@@ -14,10 +15,10 @@ class TaprootSpec extends FunSuite {
1415
val (_, master) = DeterministicWallet.ExtendedPrivateKey.decode("tprv8ZgxMBicQKsPeQQADibg4WF7mEasy3piWZUHyThAzJCPNgMHDVYhTCVfev3jFbDhcYm4GimeFMbbi9z1d9rfY1aL5wfJ9mNebQ4thJ62EJb")
1516
val key = DeterministicWallet.derivePrivateKey(master, "86'/1'/0'/0/1")
1617
val internalKey = key.publicKey.xOnly
17-
val script = Script.pay2tr(internalKey, scripts_opt = None)
18-
val (outputKey, _) = internalKey.outputKey(TaprootTweak.NoScriptTweak)
18+
val script = Script.pay2tr(internalKey, KeyPathTweak)
19+
val (outputKey, _) = internalKey.outputKey(TaprootTweak.KeyPathTweak)
1920
assert("tb1phlhs7afhqzkgv0n537xs939s687826vn8l24ldkrckvwsnlj3d7qj6u57c" == Bech32.encodeWitnessAddress("tb", 1, outputKey.pub.value.toByteArray))
20-
assert(script == Script.pay2tr(outputKey))
21+
assert(addressFromPublicKeyScript(Block.Testnet3GenesisBlock.hash, script).contains("tb1phlhs7afhqzkgv0n537xs939s687826vn8l24ldkrckvwsnlj3d7qj6u57c"))
2122

2223
// tx sends to tb1phlhs7afhqzkgv0n537xs939s687826vn8l24ldkrckvwsnlj3d7qj6u57c
2324
val tx = Transaction.read(
@@ -41,31 +42,31 @@ class TaprootSpec extends FunSuite {
4142
assert(Crypto.verifySignatureSchnorr(hash, sig, outputKey))
4243

4344
// re-create signature
44-
val ourSig = Crypto.signSchnorr(hash, key.privateKey, TaprootTweak.NoScriptTweak)
45+
val ourSig = Crypto.signSchnorr(hash, key.privateKey, Some(TaprootTweak.KeyPathTweak))
4546
assert(Crypto.verifySignatureSchnorr(hash, ourSig, outputKey))
4647
assert(Secp256k1.get().verifySchnorr(ourSig.toArray, hash.toArray, outputKey.pub.value.toByteArray))
4748

4849
// setting auxiliary random data to all-zero yields the same result as not setting any auxiliary random data
49-
val ourSig1 = Crypto.signSchnorr(hash, key.privateKey, TaprootTweak.NoScriptTweak, Some(ByteVector32.Zeroes))
50+
val ourSig1 = Crypto.signSchnorr(hash, key.privateKey, Some(TaprootTweak.KeyPathTweak), Some(ByteVector32.Zeroes))
5051
assert(ourSig == ourSig1)
5152

5253
// setting auxiliary random data to a non-zero value yields a different result
53-
val ourSig2 = Crypto.signSchnorr(hash, key.privateKey, TaprootTweak.NoScriptTweak, Some(ByteVector32.One))
54+
val ourSig2 = Crypto.signSchnorr(hash, key.privateKey, Some(TaprootTweak.KeyPathTweak), Some(ByteVector32.One))
5455
assert(ourSig != ourSig2)
5556
}
5657

5758
test("send to and spend from taproot addresses") {
5859
val privateKey = PrivateKey(ByteVector32.fromValidHex("0101010101010101010101010101010101010101010101010101010101010101"))
5960
val internalKey = privateKey.publicKey.xOnly
60-
val (outputKey, _) = internalKey.outputKey(TaprootTweak.NoScriptTweak)
61+
val (outputKey, _) = internalKey.outputKey(TaprootTweak.KeyPathTweak)
6162
val address = Bech32.encodeWitnessAddress("tb", 1, outputKey.pub.value.toByteArray)
6263
assert("tb1p33wm0auhr9kkahzd6l0kqj85af4cswn276hsxg6zpz85xe2r0y8snwrkwy" == address)
6364

6465
// this tx sends to tb1p33wm0auhr9kkahzd6l0kqj85af4cswn276hsxg6zpz85xe2r0y8snwrkwy
6566
val tx = Transaction.read(
6667
"02000000000101bf77ef36f2c0f32e0822cef0514948254997495a34bfba7dd4a73aabfcbb87900000000000fdffffff02c2c2000000000000160014b5c3dbfeb8e7d0c809c3ba3f815fd430777ef4be50c30000000000002251208c5db7f797196d6edc4dd7df6048f4ea6b883a6af6af032342088f436543790f0140583f758bea307216e03c1f54c3c6088e8923c8e1c89d96679fb00de9e808a79d0fba1cc3f9521cb686e8f43fb37cc6429f2e1480c70cc25ecb4ac0dde8921a01f1f70000"
6768
)
68-
assert(Script.pay2tr(internalKey, scripts_opt = None) == Script.parse(tx.txOut(1).publicKeyScript))
69+
assert(Script.pay2tr(internalKey, KeyPathTweak) == Script.parse(tx.txOut(1).publicKeyScript))
6970

7071
// we want to spend
7172
val Right(outputScript) = addressToPublicKeyScript(Block.Testnet3GenesisBlock.hash, "tb1pn3g330w4n5eut7d4vxq0pp303267qc6vg8d2e0ctjuqre06gs3yqnc5yx0")
@@ -157,7 +158,7 @@ class TaprootSpec extends FunSuite {
157158
val internalPubkey = PublicKey.fromBin(ByteVector.fromValidHex("0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")).xOnly
158159

159160
// funding tx sends to our tapscript
160-
val fundingTx = Transaction(version = 2, txIn = Nil, txOut = Seq(TxOut(Satoshi(1000000), Script.pay2tr(internalPubkey, Some(scriptTree)))), lockTime = 0)
161+
val fundingTx = Transaction(version = 2, txIn = Nil, txOut = Seq(TxOut(Satoshi(1000000), Script.pay2tr(internalPubkey, scriptTree))), lockTime = 0)
161162

162163
// create an unsigned transaction
163164
val tmp = Transaction(
@@ -213,7 +214,7 @@ class TaprootSpec extends FunSuite {
213214
val (tweakedKey, _) = internalPubkey.outputKey(scriptTree)
214215

215216
// this is the tapscript we send funds to
216-
val script = Script.pay2tr(internalPubkey, Some(scriptTree))
217+
val script = Script.pay2tr(internalPubkey, scriptTree)
217218
val bip350Address = Bech32.encodeWitnessAddress(Bech32.hrp(blockchain), 1.toByte, tweakedKey.pub.value.toByteArray)
218219
assert(bip350Address == "tb1p78gx95syx0qz8w5nftk8t7nce78zlpqpsxugcvq5xpfy4tvn6rasd7wk0y")
219220
val Right(sweepPublicKeyScript) = addressToPublicKeyScript(blockchain, "tb1qxy9hhxkw7gt76qrm4yzw4j06gkk4evryh8ayp7")

0 commit comments

Comments
 (0)