diff --git a/pom.xml b/pom.xml index 3ef1d507..99ad3f52 100644 --- a/pom.xml +++ b/pom.xml @@ -146,12 +146,12 @@ fr.acinq.bitcoin bitcoin-kmp-jvm - 0.29.0 + 0.30.0 fr.acinq.secp256k1 secp256k1-kmp-jni-jvm - 0.22.0 + 0.23.0 org.scodec diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala index bfb56357..8b6cd3d8 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala @@ -240,10 +240,6 @@ object Crypto { def isDefinedHashTypeSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isDefinedHashTypeSignature(sig.toArray) - def compact2der(signature: ByteVector64): ByteVector = bitcoin.Crypto.compact2der(signature) - - def der2compact(signature: ByteVector): ByteVector64 = bitcoin.Crypto.der2compact(signature.toArray) - /** * @param data data * @param signature signature diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala index e01ad5ca..1db20523 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala @@ -282,7 +282,7 @@ object Transaction extends BtcSerializer[Transaction] { * @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 + * @return the encoded signature (DER + sighash byte) 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 = { tx.signInput(inputIndex, previousOutputScript, sighashType, amount, signatureVersion, privateKey) @@ -298,11 +298,18 @@ object Transaction extends BtcSerializer[Transaction] { * @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 + * @return the encoded signature (DER + sighash byte) of this tx for this specific tx input */ def signInput(tx: Transaction, inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector = signInput(tx, inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion, privateKey) + /** + * @param sig ECDSA signature in compact format + * @param sighashType signature hash type, which will be appended to the signature + * @return an ECDSA signature in the format used in transaction witnesses: DER encoded followed by a sighash byte + */ + def encodeWitnessEcdsaSig(sig: ByteVector64, sighashType: Int): ByteVector = ByteVector.view(fr.acinq.bitcoin.Transaction.encodeWitnessEcdsaSig(sig, sighashType)) + /** * Sign a taproot tx input, using the internal key path. * @@ -484,6 +491,36 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi 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 in compact 64 bytes format + */ + def signInputCompact(inputIndex: Int, previousOutputScript: ByteVector, sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector64 = { + scala2kmp(this).signInputCompact(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 in compact 64 bytes format + */ + def signInputCompact(inputIndex: Int, previousOutputScript: Seq[ScriptElt], sighashType: Int, amount: Satoshi, signatureVersion: Int, privateKey: PrivateKey): ByteVector64 = + signInputCompact(inputIndex, Script.write(previousOutputScript), sighashType, amount, signatureVersion, privateKey) + + /** * sign a tx input * diff --git a/src/test/scala/fr/acinq/bitcoin/scalacompat/CryptoSpec.scala b/src/test/scala/fr/acinq/bitcoin/scalacompat/CryptoSpec.scala index 0f157d03..7761f892 100644 --- a/src/test/scala/fr/acinq/bitcoin/scalacompat/CryptoSpec.scala +++ b/src/test/scala/fr/acinq/bitcoin/scalacompat/CryptoSpec.scala @@ -3,6 +3,7 @@ package fr.acinq.bitcoin.scalacompat import fr.acinq.bitcoin.Base58.Prefix import fr.acinq.bitcoin.scalacompat.Crypto._ import fr.acinq.bitcoin.{Base58, Base58Check} +import fr.acinq.secp256k1.Secp256k1 import org.scalatest.FlatSpec import scodec.bits._ @@ -157,7 +158,7 @@ class CryptoSpec extends FlatSpec { dataset.map { case (k, m, s) => - val sig: ByteVector = Crypto.compact2der(Crypto.sign(Crypto.sha256(ByteVector.view(m.getBytes("UTF-8"))), PrivateKey(k))) + val sig: ByteVector = ByteVector.view(Secp256k1.get().compact2der(Crypto.sign(Crypto.sha256(ByteVector.view(m.getBytes("UTF-8"))), PrivateKey(k)).toArray)) assert(sig == s) } } diff --git a/src/test/scala/fr/acinq/bitcoin/scalacompat/Secp256k1Spec.scala b/src/test/scala/fr/acinq/bitcoin/scalacompat/Secp256k1Spec.scala index 1e611a55..a52573c0 100644 --- a/src/test/scala/fr/acinq/bitcoin/scalacompat/Secp256k1Spec.scala +++ b/src/test/scala/fr/acinq/bitcoin/scalacompat/Secp256k1Spec.scala @@ -5,7 +5,7 @@ import fr.acinq.secp256k1.Secp256k1 import org.scalatest.FunSuite import scodec.bits.ByteVector -import scala.util.{Random, Try} +import scala.util.Random /** * run this test with -Djava.library.path=$PATH_LIBSECP256K1_DIR where $PATH_LIBSECP256K1_DIR is a directory that @@ -33,7 +33,7 @@ class Secp256k1Spec extends FunSuite { Random.nextBytes(priv) Random.nextBytes(data) val sig1: ByteVector = Crypto.sign(ByteVector.view(data), PrivateKey(ByteVector.view(priv))) - val sig2: ByteVector = ByteVector.view(nativeSecp256k1.get.sign(data, priv)) + val sig2: ByteVector = ByteVector.view(nativeSecp256k1.get.sign(data, priv, null)) assert(sig1 == sig2) } } diff --git a/src/test/scala/fr/acinq/bitcoin/scalacompat/SegwitSpec.scala b/src/test/scala/fr/acinq/bitcoin/scalacompat/SegwitSpec.scala index 2002d6cf..9110f20d 100644 --- a/src/test/scala/fr/acinq/bitcoin/scalacompat/SegwitSpec.scala +++ b/src/test/scala/fr/acinq/bitcoin/scalacompat/SegwitSpec.scala @@ -2,6 +2,7 @@ package fr.acinq.bitcoin.scalacompat import fr.acinq.bitcoin.scalacompat.Crypto.PrivateKey import fr.acinq.bitcoin.{Base58, Base58Check, ScriptFlags, SigHash, SigVersion} +import fr.acinq.secp256k1.Secp256k1 import org.scalatest.FunSuite import scodec.bits._ @@ -30,7 +31,8 @@ class SegwitSpec extends FunSuite { val priv = PrivateKey(hex"619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb901") val pub = priv.publicKey val sig = hex"304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee" - assert(Crypto.verifySignature(hash, Crypto.der2compact(sig), pub)) + val compact = ByteVector64(ByteVector(Secp256k1.get().der2compact(sig.toArray))) + assert(Crypto.verifySignature(hash, ByteVector64(compact), pub)) val sigScript = hex"4830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01" val tx1 = tx.updateSigScript(0, sigScript)