Skip to content

Commit f4d6a35

Browse files
authored
Use bitcoin-kmp 0.20.0 (#88)
Use bitcoin-kmp 0.20.0
1 parent fb04b05 commit f4d6a35

13 files changed

+245
-100
lines changed

.github/workflows/main.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ jobs:
1313
timeout-minutes: 15
1414

1515
steps:
16-
- uses: actions/checkout@v2
16+
- uses: actions/checkout@v4
1717

1818
- name: Set up JDK 11
19-
uses: actions/setup-java@v1
19+
uses: actions/setup-java@v4
2020
with:
2121
java-version: 11
22-
22+
distribution: 'adopt'
23+
2324
- name: Cache Maven dependencies
24-
uses: actions/cache@v2
25+
uses: actions/cache@v4
2526
with:
2627
path: ~/.m2
2728
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

pom.xml

+2-7
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,12 @@
171171
<dependency>
172172
<groupId>fr.acinq.bitcoin</groupId>
173173
<artifactId>bitcoin-kmp-jvm</artifactId>
174-
<version>0.18.0</version>
174+
<version>0.20.0</version>
175175
</dependency>
176176
<dependency>
177177
<groupId>fr.acinq.secp256k1</groupId>
178178
<artifactId>secp256k1-kmp-jni-jvm</artifactId>
179-
<version>0.14.0</version>
180-
</dependency>
181-
<dependency>
182-
<groupId>org.jetbrains.kotlin</groupId>
183-
<artifactId>kotlin-stdlib-jdk8</artifactId>
184-
<version>1.9.22</version>
179+
<version>0.15.0</version>
185180
</dependency>
186181
<dependency>
187182
<groupId>org.scodec</groupId>

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

+8-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@ object Block {
2727
val hash: BlockHash = fr.acinq.bitcoin.Block.LivenetGenesisBlock.hash
2828
}
2929

30-
object TestnetGenesisBlock {
31-
val blockId: BlockId = fr.acinq.bitcoin.Block.TestnetGenesisBlock.blockId
32-
val hash: BlockHash = fr.acinq.bitcoin.Block.TestnetGenesisBlock.hash
30+
object Testnet3GenesisBlock {
31+
val blockId: BlockId = fr.acinq.bitcoin.Block.Testnet3GenesisBlock.blockId
32+
val hash: BlockHash = fr.acinq.bitcoin.Block.Testnet3GenesisBlock.hash
33+
}
34+
35+
object Testnet4GenesisBlock {
36+
val blockId: BlockId = fr.acinq.bitcoin.Block.Testnet4GenesisBlock.blockId
37+
val hash: BlockHash = fr.acinq.bitcoin.Block.Testnet4GenesisBlock.hash
3338
}
3439

3540
object RegtestGenesisBlock {

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

+27-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ object DeterministicWallet {
5454

5555
def publicKey: PublicKey = privateKey.publicKey
5656

57+
def extendedPublicKey: ExtendedPublicKey = ExtendedPublicKey(priv.getExtendedPublicKey)
58+
59+
def derivePrivateKey(index: Long): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(index))
60+
61+
def derivePrivateKey(path: Seq[Long]): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path.map(x => Long.box(x)).toList.asJava))
62+
63+
def derivePrivateKey(path: KeyPath): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path))
64+
65+
def derivePrivateKey(path: String): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path))
66+
67+
def encode(prefix: Int): String = priv.encode(prefix)
68+
69+
def fingerprint: Long = priv.fingerprint()
70+
5771
override def toString: String = priv.toString
5872
}
5973

@@ -68,7 +82,7 @@ object DeterministicWallet {
6882
}
6983
}
7084

71-
def encode(input: ExtendedPrivateKey, prefix: Int): String = bitcoin.DeterministicWallet.encode(input.priv, prefix)
85+
def encode(input: ExtendedPrivateKey, prefix: Int): String = input.encode(prefix)
7286

7387
case class ExtendedPublicKey(pub: bitcoin.DeterministicWallet.ExtendedPublicKey) {
7488
val publickeybytes: ByteVector = pub.publickeybytes
@@ -79,6 +93,18 @@ object DeterministicWallet {
7993

8094
def publicKey: PublicKey = pub.getPublicKey
8195

96+
def derivePublicKey(index: Long): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(index))
97+
98+
def derivePublicKey(path: Seq[Long]): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path.map(x => Long.box(x)).toList.asJava))
99+
100+
def derivePublicKey(path: KeyPath): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path))
101+
102+
def derivePublicKey(path: String): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path))
103+
104+
def encode(prefix: Int): String = pub.encode(prefix)
105+
106+
def fingerprint: Long = pub.fingerprint()
107+
82108
override def toString: String = pub.toString
83109
}
84110

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

+134-15
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ object Transaction extends BtcSerializer[Transaction] {
192192
* @return a new transaction with proper inputs and outputs according to SIGHASH_TYPE rules
193193
*/
194194
def prepareForSigning(tx: Transaction, inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): Transaction = {
195-
fr.acinq.bitcoin.Transaction.prepareForSigning(tx, inputIndex, previousOutputScript.toArray, sighashType)
195+
tx.prepareForSigning(inputIndex, previousOutputScript, sighashType)
196196
}
197197

198198
/**
@@ -231,7 +231,7 @@ object Transaction extends BtcSerializer[Transaction] {
231231
* @return a hash which can be used to sign the referenced tx input
232232
*/
233233
def hashForSigning(tx: Transaction, inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int): ByteVector32 = {
234-
ByteVector32(ByteVector.view(fr.acinq.bitcoin.Transaction.hashForSigning(tx, inputIndex, previousOutputScript.toArray, sighashType, amount, signatureVersion)))
234+
tx.hashForSigning(inputIndex, previousOutputScript, sighashType, amount, signatureVersion)
235235
}
236236

237237
/**
@@ -257,17 +257,17 @@ object Transaction extends BtcSerializer[Transaction] {
257257
* @param annex_opt (optional) taproot annex
258258
*/
259259
def hashForSigningSchnorr(tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, sigVersion: Int, tapleaf_opt: Option[ByteVector32] = None, annex_opt: Option[ByteVector] = None): ByteVector32 = {
260-
bitcoin.Transaction.hashForSigningSchnorr(tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, sigVersion, tapleaf_opt.map(scala2kmp).orNull, annex_opt.map(scala2kmp).orNull, null)
260+
tx.hashForSigningSchnorr(inputIndex, inputs, sighashType, sigVersion, tapleaf_opt, annex_opt)
261261
}
262262

263263
/** Use this function when spending a taproot key path. */
264264
def hashForSigningTaprootKeyPath(tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, annex_opt: Option[ByteVector] = None): ByteVector32 = {
265-
bitcoin.Transaction.hashForSigningTaprootKeyPath(tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, annex_opt.map(scala2kmp).orNull)
265+
tx.hashForSigningTaprootKeyPath(inputIndex, inputs, sighashType, annex_opt)
266266
}
267267

268268
/** Use this function when spending a taproot script path. */
269269
def hashForSigningTaprootScriptPath(tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None): ByteVector32 = {
270-
bitcoin.Transaction.hashForSigningTaprootScriptPath(tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scala2kmp(tapleaf), annex_opt.map(scala2kmp).orNull)
270+
tx.hashForSigningTaprootScriptPath(inputIndex, inputs, sighashType, tapleaf, annex_opt)
271271
}
272272

273273
/**
@@ -283,7 +283,7 @@ object Transaction extends BtcSerializer[Transaction] {
283283
* @return the encoded signature of this tx for this specific tx input
284284
*/
285285
def signInput(tx: Transaction, inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector = {
286-
ByteVector.view(fr.acinq.bitcoin.Transaction.signInput(tx, inputIndex, scala2kmp(previousOutputScript), sighashType, amount, signatureVersion, privateKey.priv))
286+
tx.signInput(inputIndex, previousOutputScript, sighashType, amount, signatureVersion, privateKey)
287287
}
288288

289289
/**
@@ -313,7 +313,7 @@ object Transaction extends BtcSerializer[Transaction] {
313313
* @return the schnorr signature of this tx for this specific tx input.
314314
*/
315315
def signInputTaprootKeyPath(privateKey: PrivateKey, tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, scriptTree_opt: Option[bitcoin.ScriptTree], annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
316-
bitcoin.Transaction.signInputTaprootKeyPath(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
316+
tx.signInputTaprootKeyPath(privateKey, inputIndex, inputs, sighashType, scriptTree_opt, annex_opt, auxrand32)
317317
}
318318

319319
/**
@@ -328,20 +328,15 @@ object Transaction extends BtcSerializer[Transaction] {
328328
* @return the schnorr signature of this tx for this specific tx input and the given script leaf.
329329
*/
330330
def signInputTaprootScriptPath(privateKey: PrivateKey, tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
331-
bitcoin.Transaction.signInputTaprootScriptPath(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, tapleaf, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
331+
tx.signInputTaprootScriptPath(privateKey, inputIndex, inputs, sighashType, tapleaf, annex_opt, auxrand32)
332332
}
333333

334334
def correctlySpends(tx: Transaction, previousOutputs: Map[OutPoint, TxOut], scriptFlags: Int): Unit = {
335-
fr.acinq.bitcoin.Transaction.correctlySpends(tx, previousOutputs.map { case (o, t) => scala2kmp(o) -> scala2kmp(t) }.asJava, scriptFlags)
335+
tx.correctlySpends(previousOutputs, scriptFlags)
336336
}
337337

338338
def correctlySpends(tx: Transaction, inputs: Seq[Transaction], scriptFlags: Int): Unit = {
339-
val prevouts = tx.txIn.map(_.outPoint).map(outpoint => {
340-
val prevTx = inputs.find(_.txid == outpoint.txid).get
341-
val prevOutput = prevTx.txOut(outpoint.index.toInt)
342-
outpoint -> prevOutput
343-
}).toMap
344-
correctlySpends(tx, prevouts, scriptFlags)
339+
tx.correctlySpends(inputs, scriptFlags)
345340
}
346341
}
347342

@@ -428,5 +423,129 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi
428423

429424
def weight(protocolVersion: Long = PROTOCOL_VERSION): Int = Transaction.weight(this, protocolVersion)
430425

426+
/**
427+
* prepare a transaction for signing a specific input
428+
*
429+
* @param inputIndex index of the tx input that is being processed
430+
* @param previousOutputScript public key script of the output claimed by this tx input
431+
* @param sighashType signature hash type
432+
* @return a new transaction with proper inputs and outputs according to SIGHASH_TYPE rules
433+
*/
434+
def prepareForSigning(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): Transaction = {
435+
scala2kmp(this).prepareForSigning(inputIndex, previousOutputScript.toArray, sighashType)
436+
}
437+
438+
/**
439+
* hash a tx for signing
440+
*
441+
* @param inputIndex index of the tx input that is being processed
442+
* @param previousOutputScript public key script of the output claimed by this tx input
443+
* @param sighashType signature hash type
444+
* @param amount amount of the output claimed by this input
445+
* @return a hash which can be used to sign the referenced tx input
446+
*/
447+
def hashForSigning(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int): ByteVector32 = {
448+
ByteVector32(ByteVector.view(scala2kmp(this).hashForSigning(inputIndex, previousOutputScript.toArray, sighashType, amount, signatureVersion)))
449+
}
450+
451+
/**
452+
* hash a tx for signing
453+
*
454+
* @param inputIndex index of the tx input that is being processed
455+
* @param previousOutputScript public key script of the output claimed by this tx input
456+
* @param sighashType signature hash type
457+
* @param amount amount of the output claimed by this input
458+
* @return a hash which can be used to sign the referenced tx input
459+
*/
460+
def hashForSigning(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int): ByteVector32 =
461+
hashForSigning(inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion)
462+
463+
/**
464+
* @param inputIndex index of the transaction input being signed
465+
* @param inputs UTXOs spent by this transaction
466+
* @param sighashType signature hash type
467+
* @param sigVersion signature version
468+
* @param tapleaf_opt when spending a tapscript, the hash of the corresponding script leaf must be provided
469+
* @param annex_opt (optional) taproot annex
470+
*/
471+
def hashForSigningSchnorr(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, sigVersion: Int, tapleaf_opt: Option[ByteVector32] = None, annex_opt: Option[ByteVector] = None): ByteVector32 = {
472+
scala2kmp(this).hashForSigningSchnorr(inputIndex, inputs.map(scala2kmp).asJava, sighashType, sigVersion, tapleaf_opt.map(scala2kmp).orNull, annex_opt.map(scala2kmp).orNull, null)
473+
}
474+
475+
/** Use this function when spending a taproot key path. */
476+
def hashForSigningTaprootKeyPath(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, annex_opt: Option[ByteVector] = None): ByteVector32 = {
477+
scala2kmp(this).hashForSigningTaprootKeyPath(inputIndex, inputs.map(scala2kmp).asJava, sighashType, annex_opt.map(scala2kmp).orNull)
478+
}
479+
480+
/** Use this function when spending a taproot script path. */
481+
def hashForSigningTaprootScriptPath(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None): ByteVector32 = {
482+
scala2kmp(this).hashForSigningTaprootScriptPath(inputIndex, inputs.map(scala2kmp).asJava, sighashType, scala2kmp(tapleaf), annex_opt.map(scala2kmp).orNull)
483+
}
484+
485+
/**
486+
* sign a tx input
487+
*
488+
* @param inputIndex index of the tx input that is being processed
489+
* @param previousOutputScript public key script of the output claimed by this tx input
490+
* @param sighashType signature hash type, which will be appended to the signature
491+
* @param amount amount of the output claimed by this tx input
492+
* @param signatureVersion signature version (1: segwit, 0: pre-segwit)
493+
* @param privateKey private key
494+
* @return the encoded signature of this tx for this specific tx input
495+
*/
496+
def signInput(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector = {
497+
ByteVector.view(scala2kmp(this).signInput(inputIndex, scala2kmp(previousOutputScript), sighashType, amount, signatureVersion, privateKey.priv))
498+
}
499+
500+
/**
501+
* sign a tx input
502+
*
503+
* @param inputIndex index of the tx input that is being processed
504+
* @param previousOutputScript public key script of the output claimed by this tx input
505+
* @param sighashType signature hash type, which will be appended to the signature
506+
* @param amount amount of the output claimed by this tx input
507+
* @param signatureVersion signature version (1: segwit, 0: pre-segwit)
508+
* @param privateKey private key
509+
* @return the encoded signature of this tx for this specific tx input
510+
*/
511+
def signInput(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector =
512+
signInput(inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion, privateKey)
513+
514+
/**
515+
* Sign a taproot tx input, using the internal key path.
516+
*
517+
* @param privateKey private key.
518+
* @param inputIndex index of the tx input that is being signed.
519+
* @param inputs list of all UTXOs spent by this transaction.
520+
* @param sighashType signature hash type, which will be appended to the signature (if not default).
521+
* @param scriptTree_opt tapscript tree of the signed input, if it has script paths.
522+
* @return the schnorr signature of this tx for this specific tx input.
523+
*/
524+
def signInputTaprootKeyPath(privateKey: PrivateKey, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, scriptTree_opt: Option[bitcoin.ScriptTree], annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
525+
scala2kmp(this).signInputTaprootKeyPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
526+
}
527+
528+
/**
529+
* Sign a taproot tx input, using one of its script paths.
530+
*
531+
* @param privateKey private key.
532+
* @param inputIndex index of the tx input that is being signed.
533+
* @param inputs list of all UTXOs spent by this transaction.
534+
* @param sighashType signature hash type, which will be appended to the signature (if not default).
535+
* @param tapleaf tapscript leaf hash of the script that is being spent.
536+
* @return the schnorr signature of this tx for this specific tx input and the given script leaf.
537+
*/
538+
def signInputTaprootScriptPath(privateKey: PrivateKey, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
539+
scala2kmp(this).signInputTaprootScriptPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, tapleaf, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
540+
}
541+
542+
def correctlySpends(previousOutputs: Map[OutPoint, TxOut], scriptFlags: Int): Unit = {
543+
scala2kmp(this).correctlySpends(previousOutputs.map { case (o, t) => scala2kmp(o) -> scala2kmp(t) }.asJava, scriptFlags)
544+
}
545+
546+
def correctlySpends(inputs: Seq[Transaction], scriptFlags: Int): Unit = {
547+
scala2kmp(this).correctlySpends(inputs.map(scala2kmp).asJava, scriptFlags)
548+
}
549+
431550
override def serializer: BtcSerializer[Transaction] = Transaction
432551
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ class BIP49Spec extends FunSuite {
2424
assert(key.privateKey.toBase58(Base58.Prefix.SecretKeyTestnet) == "cULrpoZGXiuC19Uhvykx7NugygA3k86b3hmdCeyvHYQZSxojGyXJ")
2525
assert(key.privateKey == PrivateKey(hex"0xc9bdb49cfbaedca21c4b1f3a7803c34636b1d7dc55a717132443fc3f4c5867e801"))
2626
assert(key.publicKey == PublicKey(hex"0x03a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f"))
27-
assert(computeBIP49Address(key.publicKey, Block.TestnetGenesisBlock.hash) == "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2")
27+
assert(computeBIP49Address(key.publicKey, Block.Testnet3GenesisBlock.hash) == "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2")
2828
}
2929
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ object DeriveWalletKeysSpec {
5757
for (i <- 0L until 5L) yield {
5858
val pub = DeterministicWallet.derivePublicKey(master, 0L :: i :: Nil)
5959
val address = prefix match {
60-
case DeterministicWallet.tpub if derivationScheme.contains(BIP44) => computeBIP44Address(pub.publicKey, Block.TestnetGenesisBlock.hash)
61-
case DeterministicWallet.tpub if derivationScheme.contains(BIP49) => computeBIP49Address(pub.publicKey, Block.TestnetGenesisBlock.hash)
62-
case DeterministicWallet.upub => computeBIP49Address(pub.publicKey, Block.TestnetGenesisBlock.hash)
63-
case DeterministicWallet.vpub => computeBIP84Address(pub.publicKey, Block.TestnetGenesisBlock.hash)
60+
case DeterministicWallet.tpub if derivationScheme.contains(BIP44) => computeBIP44Address(pub.publicKey, Block.Testnet3GenesisBlock.hash)
61+
case DeterministicWallet.tpub if derivationScheme.contains(BIP49) => computeBIP49Address(pub.publicKey, Block.Testnet3GenesisBlock.hash)
62+
case DeterministicWallet.upub => computeBIP49Address(pub.publicKey, Block.Testnet3GenesisBlock.hash)
63+
case DeterministicWallet.vpub => computeBIP84Address(pub.publicKey, Block.Testnet3GenesisBlock.hash)
6464
case DeterministicWallet.xpub if derivationScheme.contains(BIP44) => computeBIP44Address(pub.publicKey, Block.LivenetGenesisBlock.hash)
6565
case DeterministicWallet.xpub if derivationScheme.contains(BIP49) => computeBIP49Address(pub.publicKey, Block.LivenetGenesisBlock.hash)
6666
case DeterministicWallet.ypub => computeBIP49Address(pub.publicKey, Block.LivenetGenesisBlock.hash)

0 commit comments

Comments
 (0)