Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use bitcoin-kmp 0.20.0 #88

Merged
merged 2 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ jobs:
timeout-minutes: 15

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up JDK 11
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
java-version: 11

distribution: 'adopt'

- name: Cache Maven dependencies
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
Expand Down
9 changes: 2 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,12 @@
<dependency>
<groupId>fr.acinq.bitcoin</groupId>
<artifactId>bitcoin-kmp-jvm</artifactId>
<version>0.18.0</version>
<version>0.20.0</version>
</dependency>
<dependency>
<groupId>fr.acinq.secp256k1</groupId>
<artifactId>secp256k1-kmp-jni-jvm</artifactId>
<version>0.14.0</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.9.22</version>
<version>0.15.0</version>
</dependency>
<dependency>
<groupId>org.scodec</groupId>
Expand Down
11 changes: 8 additions & 3 deletions src/main/scala/fr/acinq/bitcoin/scalacompat/Block.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ object Block {
val hash: BlockHash = fr.acinq.bitcoin.Block.LivenetGenesisBlock.hash
}

object TestnetGenesisBlock {
val blockId: BlockId = fr.acinq.bitcoin.Block.TestnetGenesisBlock.blockId
val hash: BlockHash = fr.acinq.bitcoin.Block.TestnetGenesisBlock.hash
object Testnet3GenesisBlock {
val blockId: BlockId = fr.acinq.bitcoin.Block.Testnet3GenesisBlock.blockId
val hash: BlockHash = fr.acinq.bitcoin.Block.Testnet3GenesisBlock.hash
}

object Testnet4GenesisBlock {
val blockId: BlockId = fr.acinq.bitcoin.Block.Testnet4GenesisBlock.blockId
val hash: BlockHash = fr.acinq.bitcoin.Block.Testnet4GenesisBlock.hash
}

object RegtestGenesisBlock {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ object DeterministicWallet {

def publicKey: PublicKey = privateKey.publicKey

def extendedPublicKey: ExtendedPublicKey = ExtendedPublicKey(priv.getExtendedPublicKey)

def derivePrivateKey(index: Long): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(index))

def derivePrivateKey(path: Seq[Long]): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path.map(x => Long.box(x)).toList.asJava))

def derivePrivateKey(path: KeyPath): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path))

def derivePrivateKey(path: String): ExtendedPrivateKey = ExtendedPrivateKey(priv.derivePrivateKey(path))

def encode(prefix: Int): String = priv.encode(prefix)

def fingerprint: Long = priv.fingerprint()

override def toString: String = priv.toString
}

Expand All @@ -68,7 +82,7 @@ object DeterministicWallet {
}
}

def encode(input: ExtendedPrivateKey, prefix: Int): String = bitcoin.DeterministicWallet.encode(input.priv, prefix)
def encode(input: ExtendedPrivateKey, prefix: Int): String = input.encode(prefix)

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

def publicKey: PublicKey = pub.getPublicKey

def derivePublicKey(index: Long): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(index))

def derivePublicKey(path: Seq[Long]): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path.map(x => Long.box(x)).toList.asJava))

def derivePublicKey(path: KeyPath): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path))

def derivePublicKey(path: String): ExtendedPublicKey = ExtendedPublicKey(pub.derivePublicKey(path))

def encode(prefix: Int): String = pub.encode(prefix)

def fingerprint: Long = pub.fingerprint()

override def toString: String = pub.toString
}

Expand Down
177 changes: 160 additions & 17 deletions src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ object Transaction extends BtcSerializer[Transaction] {
* @return a new transaction with proper inputs and outputs according to SIGHASH_TYPE rules
*/
def prepareForSigning(tx: Transaction, inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): Transaction = {
fr.acinq.bitcoin.Transaction.prepareForSigning(tx, inputIndex, previousOutputScript.toArray, sighashType)
tx.prepareForSigning(inputIndex, previousOutputScript, sighashType)
}

/**
Expand All @@ -205,7 +205,7 @@ object Transaction extends BtcSerializer[Transaction] {
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(tx: Transaction, inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): ByteVector32 = {
ByteVector32(ByteVector.view(fr.acinq.bitcoin.Transaction.hashForSigning(tx, inputIndex, previousOutputScript.toArray, sighashType)))
tx.hashForSigning(inputIndex, previousOutputScript, sighashType)
}

/**
Expand All @@ -218,7 +218,7 @@ object Transaction extends BtcSerializer[Transaction] {
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(tx: Transaction, inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int): ByteVector32 =
hashForSigning(tx, inputIndex, Script.write(previousOutputScript), sighashType)
tx.hashForSigning(inputIndex, Script.write(previousOutputScript), sighashType)

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

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

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

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

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

/**
Expand Down Expand Up @@ -313,7 +313,7 @@ object Transaction extends BtcSerializer[Transaction] {
* @return the schnorr signature of this tx for this specific tx input.
*/
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 = {
bitcoin.Transaction.signInputTaprootKeyPath(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
tx.signInputTaprootKeyPath(privateKey, inputIndex, inputs, sighashType, scriptTree_opt, annex_opt, auxrand32)
}

/**
Expand All @@ -328,20 +328,15 @@ object Transaction extends BtcSerializer[Transaction] {
* @return the schnorr signature of this tx for this specific tx input and the given script leaf.
*/
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 = {
bitcoin.Transaction.signInputTaprootScriptPath(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, sighashType, tapleaf, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
tx.signInputTaprootScriptPath(privateKey, inputIndex, inputs, sighashType, tapleaf, annex_opt, auxrand32)
}

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

def correctlySpends(tx: Transaction, inputs: Seq[Transaction], scriptFlags: Int): Unit = {
val prevouts = tx.txIn.map(_.outPoint).map(outpoint => {
val prevTx = inputs.find(_.txid == outpoint.txid).get
val prevOutput = prevTx.txOut(outpoint.index.toInt)
outpoint -> prevOutput
}).toMap
correctlySpends(tx, prevouts, scriptFlags)
tx.correctlySpends(inputs, scriptFlags)
}
}

Expand Down Expand Up @@ -428,5 +423,153 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi

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

/**
* prepare a transaction for signing a specific input
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type
* @return a new transaction with proper inputs and outputs according to SIGHASH_TYPE rules
*/
def prepareForSigning(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): Transaction = {
scala2kmp(this).prepareForSigning(inputIndex, previousOutputScript.toArray, sighashType)
}

/**
* hash a tx for signing (pre-segwit)
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int): ByteVector32 = {
ByteVector32(ByteVector.view(scala2kmp(this).hashForSigning(inputIndex, previousOutputScript.toArray, sighashType)))
}

/**
* hash a tx for signing (pre-segwit)
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int): ByteVector32 = {
ByteVector32(ByteVector.view(scala2kmp(this).hashForSigning(inputIndex, Script.write(previousOutputScript).toArray, sighashType)))
}
t-bast marked this conversation as resolved.
Show resolved Hide resolved

/**
* hash a tx for signing
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type
* @param amount amount of the output claimed by this input
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int): ByteVector32 = {
ByteVector32(ByteVector.view(scala2kmp(this).hashForSigning(inputIndex, previousOutputScript.toArray, sighashType, amount, signatureVersion)))
}

/**
* hash a tx for signing
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type
* @param amount amount of the output claimed by this input
* @return a hash which can be used to sign the referenced tx input
*/
def hashForSigning(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int): ByteVector32 =
hashForSigning(inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion)

/**
* @param inputIndex index of the transaction input being signed
* @param inputs UTXOs spent by this transaction
* @param sighashType signature hash type
* @param sigVersion signature version
* @param tapleaf_opt when spending a tapscript, the hash of the corresponding script leaf must be provided
* @param annex_opt (optional) taproot annex
*/
def hashForSigningSchnorr(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, sigVersion: Int, tapleaf_opt: Option[ByteVector32] = None, annex_opt: Option[ByteVector] = None): ByteVector32 = {
scala2kmp(this).hashForSigningSchnorr(inputIndex, inputs.map(scala2kmp).asJava, sighashType, sigVersion, tapleaf_opt.map(scala2kmp).orNull, annex_opt.map(scala2kmp).orNull, null)
}

/** Use this function when spending a taproot key path. */
def hashForSigningTaprootKeyPath(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, annex_opt: Option[ByteVector] = None): ByteVector32 = {
scala2kmp(this).hashForSigningTaprootKeyPath(inputIndex, inputs.map(scala2kmp).asJava, sighashType, annex_opt.map(scala2kmp).orNull)
}

/** Use this function when spending a taproot script path. */
def hashForSigningTaprootScriptPath(inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None): ByteVector32 = {
scala2kmp(this).hashForSigningTaprootScriptPath(inputIndex, inputs.map(scala2kmp).asJava, sighashType, scala2kmp(tapleaf), annex_opt.map(scala2kmp).orNull)
}

/**
* sign a tx input
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type, which will be appended to the signature
* @param amount amount of the output claimed by this tx input
* @param signatureVersion signature version (1: segwit, 0: pre-segwit)
* @param privateKey private key
* @return the encoded signature of this tx for this specific tx input
*/
def signInput(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector = {
ByteVector.view(scala2kmp(this).signInput(inputIndex, scala2kmp(previousOutputScript), sighashType, amount, signatureVersion, privateKey.priv))
}

/**
* sign a tx input
*
* @param inputIndex index of the tx input that is being processed
* @param previousOutputScript public key script of the output claimed by this tx input
* @param sighashType signature hash type, which will be appended to the signature
* @param amount amount of the output claimed by this tx input
* @param signatureVersion signature version (1: segwit, 0: pre-segwit)
* @param privateKey private key
* @return the encoded signature of this tx for this specific tx input
*/
def signInput(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector =
signInput(inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion, privateKey)

/**
* Sign a taproot tx input, using the internal key path.
*
* @param privateKey private key.
* @param inputIndex index of the tx input that is being signed.
* @param inputs list of all UTXOs spent by this transaction.
* @param sighashType signature hash type, which will be appended to the signature (if not default).
* @param scriptTree_opt tapscript tree of the signed input, if it has script paths.
* @return the schnorr signature of this tx for this specific tx input.
*/
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 = {
scala2kmp(this).signInputTaprootKeyPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
}

/**
* Sign a taproot tx input, using one of its script paths.
*
* @param privateKey private key.
* @param inputIndex index of the tx input that is being signed.
* @param inputs list of all UTXOs spent by this transaction.
* @param sighashType signature hash type, which will be appended to the signature (if not default).
* @param tapleaf tapscript leaf hash of the script that is being spent.
* @return the schnorr signature of this tx for this specific tx input and the given script leaf.
*/
def signInputTaprootScriptPath(privateKey: PrivateKey, inputIndex: Int, inputs: Seq[TxOut], sighashType: Int, tapleaf: ByteVector32, annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = {
scala2kmp(this).signInputTaprootScriptPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, tapleaf, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull)
}

def correctlySpends(previousOutputs: Map[OutPoint, TxOut], scriptFlags: Int): Unit = {
scala2kmp(this).correctlySpends(previousOutputs.map { case (o, t) => scala2kmp(o) -> scala2kmp(t) }.asJava, scriptFlags)
}

def correctlySpends(inputs: Seq[Transaction], scriptFlags: Int): Unit = {
scala2kmp(this).correctlySpends(inputs.map(scala2kmp).asJava, scriptFlags)
}

override def serializer: BtcSerializer[Transaction] = Transaction
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ class BIP49Spec extends FunSuite {
assert(key.privateKey.toBase58(Base58.Prefix.SecretKeyTestnet) == "cULrpoZGXiuC19Uhvykx7NugygA3k86b3hmdCeyvHYQZSxojGyXJ")
assert(key.privateKey == PrivateKey(hex"0xc9bdb49cfbaedca21c4b1f3a7803c34636b1d7dc55a717132443fc3f4c5867e801"))
assert(key.publicKey == PublicKey(hex"0x03a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f"))
assert(computeBIP49Address(key.publicKey, Block.TestnetGenesisBlock.hash) == "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2")
assert(computeBIP49Address(key.publicKey, Block.Testnet3GenesisBlock.hash) == "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2")
}
}
Loading