diff --git a/pom.xml b/pom.xml index 7b014367..d31d220d 100644 --- a/pom.xml +++ b/pom.xml @@ -10,11 +10,25 @@ - central-snapshots + central_portal https://central.sonatype.com/repository/maven-snapshots + + + central_portal + Sonatype Central Portal (snapshots) + https://central.sonatype.com/repository/maven-snapshots + + false + + + true + + + + 2020-01-01T00:00:00Z UTF-8 @@ -146,7 +160,7 @@ fr.acinq.bitcoin bitcoin-kmp-jvm - 0.30.0 + 0.31.0 fr.acinq.secp256k1 @@ -206,7 +220,7 @@ 0.8.0 true - central + central_portal https://central.sonatype.com/repository/maven-snapshots @@ -224,20 +238,6 @@ - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala index 8b6cd3d8..0b626ab7 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala @@ -40,13 +40,13 @@ object Crypto { def *(that: PrivateKey): PrivateKey = multiply(that) - def isZero: Boolean = priv.value == bitcoin.ByteVector32.Zeroes + def isZero: Boolean = priv.value.equals(fr.acinq.bitcoin.ByteVector32.Zeroes) def isValid: Boolean = priv.isValid def publicKey: PublicKey = PublicKey(priv.publicKey()) - def xOnlyPublicKey(): XonlyPublicKey = XonlyPublicKey(publicKey) + def xOnlyPublicKey(): XonlyPublicKey = XonlyPublicKey(priv.xOnlyPublicKey()) /** * @param prefix Private key prefix @@ -60,7 +60,7 @@ object Crypto { } object PrivateKey { - def apply(data: ByteVector): PrivateKey = PrivateKey(new bitcoin.PrivateKey(data.toArray)) + def apply(data: ByteVector): PrivateKey = PrivateKey(new bitcoin.PrivateKey(data.toArrayUnsafe)) /** * @param data serialized private key in bitcoin format @@ -127,7 +127,7 @@ object Crypto { def fromBin(input: ByteVector, checkValid: Boolean = true): PublicKey = { require(isPubKeyValidLax(input)) require(!checkValid || Crypto.isPubKeyValidStrict(input), "public key is invalid") - PublicKey(new bitcoin.PublicKey(bitcoin.PublicKey.compress(input.toArray))) + PublicKey(new bitcoin.PublicKey(bitcoin.PublicKey.compress(input.toArrayUnsafe))) } } @@ -184,7 +184,7 @@ object Crypto { */ def ecdh(priv: PrivateKey, pub: PublicKey): ByteVector32 = ByteVector32(ByteVector.view(bitcoin.Crypto.ecdh(priv.priv, pub.pub))) - def hmac512(key: ByteVector, data: ByteVector): ByteVector = ByteVector.view(bitcoin.Crypto.hmac512(key.toArray, data.toArray)) + def hmac512(key: ByteVector, data: ByteVector): ByteVector = ByteVector.view(bitcoin.Crypto.hmac512(key.toArrayUnsafe, data.toArrayUnsafe)) def sha256(x: ByteVector): ByteVector32 = ByteVector32(ByteVector.view(bitcoin.Crypto.sha256(x))) @@ -197,7 +197,7 @@ object Crypto { * @param input array of byte * @return the 160 bits BTC hash of input */ - def hash160(input: ByteVector): ByteVector = ByteVector.view(bitcoin.Crypto.hash160(input.toArray)) + def hash160(input: ByteVector): ByteVector = ByteVector.view(bitcoin.Crypto.hash160(input.toArrayUnsafe)) /** * 256 bits bitcoin hash @@ -206,15 +206,15 @@ object Crypto { * @param input array of byte * @return the 256 bits BTC hash of input */ - def hash256(input: ByteVector): ByteVector32 = ByteVector32(ByteVector.view(bitcoin.Crypto.hash256(input.toArray))) + def hash256(input: ByteVector): ByteVector32 = ByteVector32(ByteVector.view(bitcoin.Crypto.hash256(input.toArrayUnsafe))) - def isDERSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isDERSignature(sig.toArray) + def isDERSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isDERSignature(sig.toArrayUnsafe) - def isLowDERSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isLowDERSignature(sig.toArray) + def isLowDERSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isLowDERSignature(sig.toArrayUnsafe) - def checkSignatureEncoding(sig: ByteVector, flags: Int): Boolean = bitcoin.Crypto.checkSignatureEncoding(sig.toArray, flags) + def checkSignatureEncoding(sig: ByteVector, flags: Int): Boolean = bitcoin.Crypto.checkSignatureEncoding(sig.toArrayUnsafe, flags) - def checkPubKeyEncoding(key: ByteVector, flags: Int, sigVersion: Int): Boolean = bitcoin.Crypto.checkPubKeyEncoding(key.toArray, flags, sigVersion) + def checkPubKeyEncoding(key: ByteVector, flags: Int, sigVersion: Int): Boolean = bitcoin.Crypto.checkPubKeyEncoding(key.toArrayUnsafe, flags, sigVersion) /** * @param key serialized public key @@ -232,13 +232,13 @@ object Crypto { * @return true if the key is valid. This check is much more expensive than its lax version since here we check that * the public key is a valid point on the secp256k1 curve */ - def isPubKeyValidStrict(key: ByteVector): Boolean = isPubKeyValidLax(key) && bitcoin.Crypto.isPubKeyValid(key.toArray) + def isPubKeyValidStrict(key: ByteVector): Boolean = isPubKeyValidLax(key) && bitcoin.Crypto.isPubKeyValid(key.toArrayUnsafe) - def isPubKeyCompressedOrUncompressed(key: ByteVector): Boolean = bitcoin.Crypto.isPubKeyCompressedOrUncompressed(key.toArray) + def isPubKeyCompressedOrUncompressed(key: ByteVector): Boolean = bitcoin.Crypto.isPubKeyCompressedOrUncompressed(key.toArrayUnsafe) - def isPubKeyCompressed(key: ByteVector): Boolean = bitcoin.Crypto.isPubKeyCompressed(key.toArray) + def isPubKeyCompressed(key: ByteVector): Boolean = bitcoin.Crypto.isPubKeyCompressed(key.toArrayUnsafe) - def isDefinedHashTypeSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isDefinedHashTypeSignature(sig.toArray) + def isDefinedHashTypeSignature(sig: ByteVector): Boolean = bitcoin.Crypto.isDefinedHashTypeSignature(sig.toArrayUnsafe) /** * @param data data @@ -246,7 +246,7 @@ object Crypto { * @param publicKey public key * @return true is signature is valid for this data with this public key */ - def verifySignature(data: ByteVector, signature: ByteVector64, publicKey: PublicKey): Boolean = bitcoin.Crypto.verifySignature(data.toArray, signature, publicKey.pub) + def verifySignature(data: ByteVector, signature: ByteVector64, publicKey: PublicKey): Boolean = bitcoin.Crypto.verifySignature(data.toArrayUnsafe, signature, publicKey.pub) /** * @param data data @@ -274,7 +274,7 @@ object Crypto { */ def sign(data: Array[Byte], privateKey: PrivateKey): ByteVector64 = bitcoin.Crypto.sign(data, privateKey.priv) - def sign(data: ByteVector, privateKey: PrivateKey): ByteVector64 = sign(data.toArray, privateKey) + def sign(data: ByteVector, privateKey: PrivateKey): ByteVector64 = sign(data.toArrayUnsafe, privateKey) /** * Compute the Schnorr signature of data with private key @@ -296,10 +296,10 @@ object Crypto { * @param message message that was signed * @return a recovered public key */ - def recoverPublicKey(signature: ByteVector64, message: ByteVector, recoveryId: Int): PublicKey = PublicKey(bitcoin.Crypto.recoverPublicKey(signature, message.toArray, recoveryId)) + def recoverPublicKey(signature: ByteVector64, message: ByteVector, recoveryId: Int): PublicKey = PublicKey(bitcoin.Crypto.recoverPublicKey(signature, message.toArrayUnsafe, recoveryId)) def recoverPublicKey(signature: ByteVector64, message: ByteVector): (PublicKey, PublicKey) = { - val p = bitcoin.Crypto.recoverPublicKey(signature, message.toArray) + val p = bitcoin.Crypto.recoverPublicKey(signature, message.toArrayUnsafe) (PublicKey(p.getFirst), PublicKey(p.getSecond)) } diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/DeterministicWallet.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/DeterministicWallet.scala index 98a23206..b7e01f8c 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/DeterministicWallet.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/DeterministicWallet.scala @@ -119,10 +119,10 @@ object DeterministicWallet { } } - def encode(input: ExtendedPublicKey, prefix: Int): String = bitcoin.DeterministicWallet.encode(input.pub, prefix) + def encode(input: ExtendedPublicKey, prefix: Int): String = input.pub.encode(prefix) def write(input: ExtendedPublicKey, output: OutputStream): Unit = { - fr.acinq.bitcoin.DeterministicWallet.write(input.pub, OutputStreamWrapper(output)) + input.pub.write(OutputStreamWrapper(output)) } /** @@ -137,21 +137,21 @@ object DeterministicWallet { * @param input extended private key * @return the public key for this private key */ - def publicKey(input: ExtendedPrivateKey): ExtendedPublicKey = ExtendedPublicKey(bitcoin.DeterministicWallet.publicKey(input.priv)) + def publicKey(input: ExtendedPrivateKey): ExtendedPublicKey = ExtendedPublicKey(input.priv.extendedPublicKey) /** * * @param input extended public key * @return the fingerprint for this public key */ - def fingerprint(input: ExtendedPublicKey): Long = bitcoin.DeterministicWallet.fingerprint(input.pub) + def fingerprint(input: ExtendedPublicKey): Long = input.pub.fingerprint() /** * * @param input extended private key * @return the fingerprint for this private key (which is based on the corresponding public key) */ - def fingerprint(input: ExtendedPrivateKey): Long = bitcoin.DeterministicWallet.fingerprint(input.priv) + def fingerprint(input: ExtendedPrivateKey): Long = input.priv.fingerprint() /** * @@ -159,7 +159,7 @@ object DeterministicWallet { * @param index index of the child key * @return the derived private key at the specified index */ - def derivePrivateKey(parent: ExtendedPrivateKey, index: Long): ExtendedPrivateKey = ExtendedPrivateKey(bitcoin.DeterministicWallet.derivePrivateKey(parent.priv, index)) + def derivePrivateKey(parent: ExtendedPrivateKey, index: Long): ExtendedPrivateKey = ExtendedPrivateKey(parent.priv.derivePrivateKey(index)) /** * @@ -167,15 +167,15 @@ object DeterministicWallet { * @param index index of the child key * @return the derived public key at the specified index */ - def derivePublicKey(parent: ExtendedPublicKey, index: Long): ExtendedPublicKey = ExtendedPublicKey(bitcoin.DeterministicWallet.derivePublicKey(parent.pub, index)) + def derivePublicKey(parent: ExtendedPublicKey, index: Long): ExtendedPublicKey = parent.derivePublicKey(index) - def derivePrivateKey(parent: ExtendedPrivateKey, chain: Seq[Long]): ExtendedPrivateKey = chain.foldLeft(parent)(derivePrivateKey) + def derivePrivateKey(parent: ExtendedPrivateKey, chain: Seq[Long]): ExtendedPrivateKey = parent.derivePrivateKey(chain) def derivePrivateKey(parent: ExtendedPrivateKey, keyPath: KeyPath): ExtendedPrivateKey = derivePrivateKey(parent, keyPath.path) def derivePrivateKey(parent: ExtendedPrivateKey, path: String): ExtendedPrivateKey = derivePrivateKey(parent, KeyPath(path)) - def derivePublicKey(parent: ExtendedPublicKey, chain: Seq[Long]): ExtendedPublicKey = chain.foldLeft(parent)(derivePublicKey) + def derivePublicKey(parent: ExtendedPublicKey, chain: Seq[Long]): ExtendedPublicKey = parent.derivePublicKey(chain) def derivePublicKey(parent: ExtendedPublicKey, keyPath: KeyPath): ExtendedPublicKey = derivePublicKey(parent, keyPath.path) diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala index 607c8755..725ffaab 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala @@ -9,17 +9,17 @@ import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava} object KotlinUtils { - implicit def kmp2scala(input: bitcoin.ByteVector32): ByteVector32 = ByteVector32(ByteVector(input.toByteArray)) + implicit def kmp2scala(input: bitcoin.ByteVector32): ByteVector32 = ByteVector32(ByteVector.view(input.toByteArray)) - implicit def scala2kmp(input: ByteVector32): bitcoin.ByteVector32 = new bitcoin.ByteVector32(input.toArray) + implicit def scala2kmp(input: ByteVector32): bitcoin.ByteVector32 = new bitcoin.ByteVector32(input.toArrayUnsafe) - implicit def kmp2scala(input: bitcoin.ByteVector64): ByteVector64 = ByteVector64(ByteVector(input.toByteArray)) + implicit def kmp2scala(input: bitcoin.ByteVector64): ByteVector64 = ByteVector64(ByteVector.view(input.toByteArray)) - implicit def scala2kmp(input: ByteVector64): bitcoin.ByteVector64 = new bitcoin.ByteVector64(input.toArray) + implicit def scala2kmp(input: ByteVector64): bitcoin.ByteVector64 = new bitcoin.ByteVector64(input.toArrayUnsafe) - implicit def kmp2scala(input: bitcoin.ByteVector): ByteVector = ByteVector(input.toByteArray) + implicit def kmp2scala(input: bitcoin.ByteVector): ByteVector = ByteVector.view(input.toByteArray) - implicit def scala2kmp(input: ByteVector): bitcoin.ByteVector = new bitcoin.ByteVector(input.toArray) + implicit def scala2kmp(input: ByteVector): bitcoin.ByteVector = new bitcoin.ByteVector(input.toArrayUnsafe) implicit def kmp2scala(input: bitcoin.TxId): TxId = TxId(input.value) @@ -88,7 +88,7 @@ object KotlinUtils { implicit def kmp2scala(input: bitcoin.Transaction): Transaction = Transaction(input.version, input.txIn.asScala.toList.map(kmp2scala), input.txOut.asScala.toList.map(kmp2scala), input.lockTime) - implicit def scala2kmp(input: Transaction): bitcoin.Transaction = new bitcoin.Transaction(input.version, input.txIn.map(scala2kmp).asJava, input.txOut.map(scala2kmp).asJava, input.lockTime) + implicit def scala2kmp(input: Transaction): bitcoin.Transaction = input.kmp implicit def kmp2scala(input: bitcoin.PrivateKey): PrivateKey = PrivateKey(input) diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/LexicographicalOrdering.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/LexicographicalOrdering.scala index c3855689..86bda522 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/LexicographicalOrdering.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/LexicographicalOrdering.scala @@ -9,23 +9,7 @@ import scala.annotation.tailrec * see https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki */ object LexicographicalOrdering { - @tailrec - def isLessThan(a: Seq[Byte], b: Seq[Byte]): Boolean = { - if (a.isEmpty && b.isEmpty) false - else if (a.isEmpty) true - else if (b.isEmpty) false - else if (a.head == b.head) isLessThan(a.tail, b.tail) - else (a.head & 0xff) < (b.head & 0xff) - } - - @tailrec - def isLessThan(a: ByteVector, b: ByteVector): Boolean = { - if (a.isEmpty && b.isEmpty) false - else if (a.isEmpty) true - else if (b.isEmpty) false - else if (a.head == b.head) isLessThan(a.tail, b.tail) - else (a.head & 0xff) < (b.head & 0xff) - } + def isLessThan(a: ByteVector, b: ByteVector): Boolean = fr.acinq.bitcoin.LexicographicalOrdering.isLessThan(a.toArrayUnsafe, b.toArrayUnsafe) def isLessThan(a: OutPoint, b: OutPoint): Boolean = { if (a.txid == b.txid) a.index < b.index diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/MnemonicCode.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/MnemonicCode.scala index d20be732..3ab7dbeb 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/MnemonicCode.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/MnemonicCode.scala @@ -15,7 +15,7 @@ object MnemonicCode { * @param entropy input entropy * @return a list of mnemonic words that encodes the input entropy */ - def toMnemonics(entropy: ByteVector): List[String] = bitcoin.MnemonicCode.toMnemonics(entropy.toArray).asScala.toList + def toMnemonics(entropy: ByteVector): List[String] = bitcoin.MnemonicCode.toMnemonics(entropy.toArrayUnsafe).asScala.toList /** * BIP39 entropy encoding. @@ -24,7 +24,7 @@ object MnemonicCode { * @param wordlist word list (must be 2048 words long) * @return a list of mnemonic words that encodes the input entropy */ - def toMnemonics(entropy: ByteVector, wordlist: Seq[String]): List[String] = bitcoin.MnemonicCode.toMnemonics(entropy.toArray, wordlist.asJava).asScala.toList + def toMnemonics(entropy: ByteVector, wordlist: Seq[String]): List[String] = bitcoin.MnemonicCode.toMnemonics(entropy.toArrayUnsafe, wordlist.asJava).asScala.toList /** * Verify that a mnemonic seed is valid using default BIP39 word list. diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Musig2.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Musig2.scala index 2c601435..e724bb4a 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Musig2.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Musig2.scala @@ -14,7 +14,9 @@ object Musig2 { * Musig2 secret nonce, that should be treated as a private opaque blob. * This nonce must never be persisted or reused across signing sessions. */ - case class SecretNonce(inner: musig2.SecretNonce) + case class SecretNonce(inner: musig2.SecretNonce) { + def consume[T](f: Array[Byte] => T): Either[Throwable, T] = inner.consume$bitcoin_kmp((bytes: Array[Byte]) => f(bytes)) + } /** * Musig2 public nonce, that must be shared with other participants in the signing session. @@ -73,7 +75,7 @@ object Musig2 { * @param scriptTree_opt tapscript tree of the taproot input, if it has script paths. */ def signTaprootInput(privateKey: PrivateKey, tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], publicKeys: Seq[PublicKey], secretNonce: SecretNonce, publicNonces: Seq[IndividualNonce], scriptTree_opt: Option[ScriptTree]): Either[Throwable, ByteVector32] = { - musig2.Musig2.signTaprootInput(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, secretNonce.inner, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArray)).asJava, scriptTree_opt.map(scala2kmp).orNull).map(kmp2scala) + musig2.Musig2.signTaprootInput(privateKey, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, secretNonce.inner, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArrayUnsafe)).asJava, scriptTree_opt.map(scala2kmp).orNull).map(kmp2scala) } /** @@ -91,7 +93,7 @@ object Musig2 { * @return true if the partial signature is valid. */ def verifyTaprootSignature(partialSig: ByteVector32, nonce: IndividualNonce, publicKey: PublicKey, tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], publicKeys: Seq[PublicKey], publicNonces: Seq[IndividualNonce], scriptTree_opt: Option[ScriptTree]): Boolean = { - musig2.Musig2.verify(partialSig, new musig2.IndividualNonce(nonce.data.toArray), publicKey, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArray)).asJava, scriptTree_opt.map(scala2kmp).orNull) + musig2.Musig2.verify(partialSig, new musig2.IndividualNonce(nonce.data.toArrayUnsafe), publicKey, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArrayUnsafe)).asJava, scriptTree_opt.map(scala2kmp).orNull) } /** @@ -106,7 +108,7 @@ object Musig2 { * @param scriptTree_opt tapscript tree of the taproot input, if it has script paths. */ def aggregateTaprootSignatures(partialSigs: Seq[ByteVector32], tx: Transaction, inputIndex: Int, inputs: Seq[TxOut], publicKeys: Seq[PublicKey], publicNonces: Seq[IndividualNonce], scriptTree_opt: Option[ScriptTree]): Either[Throwable, ByteVector64] = { - musig2.Musig2.aggregateTaprootSignatures(partialSigs.map(scala2kmp).asJava, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArray)).asJava, scriptTree_opt.map(scala2kmp).orNull).map(kmp2scala) + musig2.Musig2.aggregateTaprootSignatures(partialSigs.map(scala2kmp).asJava, tx, inputIndex, inputs.map(scala2kmp).asJava, publicKeys.map(scala2kmp).asJava, publicNonces.map(n => new musig2.IndividualNonce(n.data.toArrayUnsafe)).asJava, scriptTree_opt.map(scala2kmp).orNull).map(kmp2scala) } } diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Protocol.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Protocol.scala index 766002f8..92b6f985 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Protocol.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Protocol.scala @@ -61,7 +61,7 @@ object Protocol { buffer.getShort & 0xFFFF } - def writeUInt16(input: Int, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt16(input, order).toArray) + def writeUInt16(input: Int, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt16(input, order).toArrayUnsafe) def writeUInt16(input: Int, order: ByteOrder): ByteVector = { val bin = new Array[Byte](2) @@ -85,7 +85,7 @@ object Protocol { input.toLong(signed = false, ByteOrdering.fromJava(order)) } - def writeUInt32(input: Long, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt32(input, order).toArray) + def writeUInt32(input: Long, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt32(input, order).toArrayUnsafe) def writeUInt32(input: Long, order: ByteOrder): ByteVector = { val bin = new Array[Byte](4) @@ -107,7 +107,7 @@ object Protocol { buffer.getLong() } - def writeUInt64(input: Long, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt64(input, order).toArray) + def writeUInt64(input: Long, out: OutputStream, order: ByteOrder = ByteOrder.LITTLE_ENDIAN): Unit = out.write(writeUInt64(input, order).toArrayUnsafe) def writeUInt64(input: Long, order: ByteOrder): ByteVector = { val bin = new Array[Byte](8) @@ -156,11 +156,11 @@ object Protocol { def writeBytes(input: Array[Byte], out: OutputStream): Unit = out.write(input) - def writeBytes(input: ByteVector, out: OutputStream): Unit = out.write(input.toArray) + def writeBytes(input: ByteVector, out: OutputStream): Unit = out.write(input.toArrayUnsafe) def varstring(input: InputStream): String = { val length = varint(input) - new String(bytes(input, length).toArray, "UTF-8") + new String(bytes(input, length).toArrayUnsafe, "UTF-8") } def writeVarstring(input: String, out: OutputStream): Unit = { @@ -234,7 +234,7 @@ trait BtcSerializer[T] { * @param in message binary data in hex format * @return a deserialized message of type T */ - def read(in: String, protocolVersion: Long): T = read(ByteVector.fromValidHex(in).toArray, protocolVersion) + def read(in: String, protocolVersion: Long): T = read(ByteVector.fromValidHex(in).toArrayUnsafe, protocolVersion) def read(in: String): T = read(in, PROTOCOL_VERSION) diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Script.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Script.scala index 41b08bc3..93d6b60d 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Script.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Script.scala @@ -11,7 +11,7 @@ object Script { import fr.acinq.bitcoin.ScriptFlags._ - def parse(blob: ByteVector): List[ScriptElt] = parse(blob.toArray) + def parse(blob: ByteVector): List[ScriptElt] = parse(blob.toArrayUnsafe) def parse(blob: Array[Byte]): List[ScriptElt] = bitcoin.Script.parse(blob).asScala.toList.map(kmp2scala) @@ -19,7 +19,7 @@ object Script { def encodeNumber(value: Long): ByteVector = ByteVector.view(bitcoin.Script.encodeNumber(value).toByteArray) - def decodeNumber(input: ByteVector, checkMinimalEncoding: Boolean, maximumSize: Int = 4): Long = bitcoin.Script.decodeNumber(input.toArray, checkMinimalEncoding, maximumSize) + def decodeNumber(input: ByteVector, checkMinimalEncoding: Boolean, maximumSize: Int = 4): Long = bitcoin.Script.decodeNumber(input.toArrayUnsafe, checkMinimalEncoding, maximumSize) def isSimpleValue(op: ScriptElt): Boolean = bitcoin.Script.isSimpleValue(op) @@ -27,7 +27,7 @@ object Script { def isPushOnly(script: Seq[ScriptElt]): Boolean = bitcoin.Script.isPushOnly(script.map(scala2kmp).asJava) - def isPayToScript(script: ByteVector): Boolean = bitcoin.Script.isPayToScript(script.toArray) + def isPayToScript(script: ByteVector): Boolean = bitcoin.Script.isPayToScript(script.toArrayUnsafe) def isNativeWitnessScript(script: Seq[ScriptElt]): Boolean = bitcoin.Script.isNativeWitnessScript(script.map(scala2kmp).asJava) @@ -65,7 +65,7 @@ object Script { private val runner = new bitcoin.Script.Runner(new bitcoin.Script.Context(context.tx, context.inputIndex, context.amount, context.prevouts.map(scala2kmp).asJava), scriptFlag) - def verifyWitnessProgram(witness: ScriptWitness, witnessVersion: Long, program: ByteVector, isP2sh: Boolean = false): Unit = runner.verifyWitnessProgram(witness, witnessVersion, program.toArray, isP2sh) + def verifyWitnessProgram(witness: ScriptWitness, witnessVersion: Long, program: ByteVector, isP2sh: Boolean = false): Unit = runner.verifyWitnessProgram(witness, witnessVersion, program.toArrayUnsafe, isP2sh) def verifyScripts(scriptSig: ByteVector, scriptPubKey: ByteVector): Boolean = verifyScripts(scriptSig, scriptPubKey, ScriptWitness.empty) @@ -106,7 +106,7 @@ object Script { * @param pubKeyHash public key hash * @return a pay-to-public-key-hash script */ - def pay2pkh(pubKeyHash: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2pkh(pubKeyHash.toArray).asScala.map(kmp2scala).toList + def pay2pkh(pubKeyHash: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2pkh(pubKeyHash.toArrayUnsafe).asScala.map(kmp2scala).toList /** * @param pubKey public key @@ -126,7 +126,7 @@ object Script { * @param script bitcoin script * @return a pay-to-script script */ - def pay2sh(script: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2sh(script.toArray).asScala.map(kmp2scala).toList + def pay2sh(script: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2sh(script.toArrayUnsafe).asScala.map(kmp2scala).toList def isPay2sh(script: Seq[ScriptElt]): Boolean = bitcoin.Script.isPay2sh(script.map(scala2kmp).asJava) @@ -140,7 +140,7 @@ object Script { * @param script bitcoin script * @return a pay-to-witness-script script */ - def pay2wsh(script: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2wsh(script.toArray).asScala.map(kmp2scala).toList + def pay2wsh(script: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2wsh(script.toArrayUnsafe).asScala.map(kmp2scala).toList def isPay2wsh(script: Seq[ScriptElt]): Boolean = bitcoin.Script.isPay2wsh(script.map(scala2kmp).asJava) @@ -148,7 +148,7 @@ object Script { * @param pubKeyHash public key hash * @return a pay-to-witness-public-key-hash script */ - def pay2wpkh(pubKeyHash: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2wpkh(pubKeyHash.toArray).asScala.map(kmp2scala).toList + def pay2wpkh(pubKeyHash: ByteVector): Seq[ScriptElt] = bitcoin.Script.pay2wpkh(pubKeyHash.toArrayUnsafe).asScala.map(kmp2scala).toList /** * @param pubKey public key diff --git a/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala b/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala index 1db20523..192edd2e 100644 --- a/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala +++ b/src/main/scala/fr/acinq/bitcoin/scalacompat/Transaction.scala @@ -161,7 +161,7 @@ object Transaction extends BtcSerializer[Transaction] { * @param version protocol version (and NOT transaction version !) * @return true if protocol version specifies that witness data is to be serialized */ - def serializeTxWitness(version: Long): Boolean = (version & bitcoin.Transaction.SERIALIZE_TRANSACTION_NO_WITNESS) == 0 + def serializeTxWitness(version: Long): Boolean = fr.acinq.bitcoin.Transaction.serializeTxWitness(version) override def read(input: InputStream, protocolVersion: Long): Transaction = { val tx = fr.acinq.bitcoin.Transaction.read(InputStreamWrapper(input), protocolVersion) @@ -207,7 +207,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))) + ByteVector32(ByteVector.view(fr.acinq.bitcoin.Transaction.hashForSigning(tx, inputIndex, previousOutputScript.toArrayUnsafe, sighashType))) } /** @@ -358,9 +358,10 @@ object Transaction extends BtcSerializer[Transaction] { * @param lockTime The block number or timestamp at which this transaction is locked */ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTime: Long) extends BtcSerializable[Transaction] { + lazy val kmp: fr.acinq.bitcoin.Transaction = new fr.acinq.bitcoin.Transaction(version, txIn.map(scala2kmp).asJava, txOut.map(scala2kmp).asJava, lockTime) // standard transaction hash, used to identify transactions (in transactions outputs for example) - lazy val hash: TxHash = TxHash(Crypto.hash256(Transaction.write(this, bitcoin.Transaction.SERIALIZE_TRANSACTION_NO_WITNESS))) + lazy val hash: TxHash = kmp.hash lazy val txid: TxId = TxId(hash) // witness transaction hash that includes witness data. used to compute the witness commitment included in the coinbase // transaction of segwit blocks @@ -426,11 +427,11 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi */ def addOutput(output: TxOut): Transaction = this.copy(txOut = this.txOut :+ output) - def baseSize(protocolVersion: Long = PROTOCOL_VERSION): Int = Transaction.baseSize(this, protocolVersion) + def baseSize(protocolVersion: Long = PROTOCOL_VERSION): Int = kmp.baseSize(protocolVersion) - def totalSize(protocolVersion: Long = PROTOCOL_VERSION): Int = Transaction.totalSize(this, protocolVersion) + def totalSize(protocolVersion: Long = PROTOCOL_VERSION): Int = kmp.totalSize(protocolVersion) - def weight(protocolVersion: Long = PROTOCOL_VERSION): Int = Transaction.weight(this, protocolVersion) + def weight(protocolVersion: Long = PROTOCOL_VERSION): Int = kmp.weight(protocolVersion) /** * prepare a transaction for signing a specific input @@ -441,7 +442,7 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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) + kmp.prepareForSigning(inputIndex, previousOutputScript.toArray, sighashType) } /** @@ -454,7 +455,7 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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))) + ByteVector32(ByteVector.view(kmp.hashForSigning(inputIndex, previousOutputScript.toArrayUnsafe, sighashType, amount, signatureVersion))) } /** @@ -478,17 +479,17 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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) + kmp.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) + kmp.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) + kmp.hashForSigningTaprootScriptPath(inputIndex, inputs.map(scala2kmp).asJava, sighashType, scala2kmp(tapleaf), annex_opt.map(scala2kmp).orNull) } /** @@ -503,7 +504,7 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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) + kmp.signInputCompact(inputIndex, scala2kmp(previousOutputScript), sighashType, amount, signatureVersion, privateKey.priv) } /** @@ -533,7 +534,7 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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)) + ByteVector.view(kmp.signInput(inputIndex, scala2kmp(previousOutputScript), sighashType, amount, signatureVersion, privateKey.priv)) } /** @@ -561,7 +562,7 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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[ScriptTree], annex_opt: Option[ByteVector] = None, auxrand32: Option[ByteVector32] = None): ByteVector64 = { - scala2kmp(this).signInputTaprootKeyPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.map(scala2kmp).orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull) + kmp.signInputTaprootKeyPath(privateKey, inputIndex, inputs.map(scala2kmp).asJava, sighashType, scriptTree_opt.map(scala2kmp).orNull, annex_opt.map(scala2kmp).orNull, auxrand32.map(scala2kmp).orNull) } /** @@ -575,15 +576,15 @@ case class Transaction(version: Long, txIn: Seq[TxIn], txOut: Seq[TxOut], lockTi * @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) + kmp.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) + kmp.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) + kmp.correctlySpends(inputs.map(scala2kmp).asJava, scriptFlags) } override def serializer: BtcSerializer[Transaction] = Transaction diff --git a/src/test/scala/fr/acinq/bitcoin/scalacompat/Musig2Spec.scala b/src/test/scala/fr/acinq/bitcoin/scalacompat/Musig2Spec.scala index ec7e4a9b..29da3c29 100644 --- a/src/test/scala/fr/acinq/bitcoin/scalacompat/Musig2Spec.scala +++ b/src/test/scala/fr/acinq/bitcoin/scalacompat/Musig2Spec.scala @@ -116,4 +116,10 @@ class Musig2Spec extends FunSuite { assert(nonce.publicNonce.data == hex"0271efb262c0535e921efacacd30146fa93f193689e4974d5348fa9d909d90000702a049680ef3f6acfb12320297df31d3a634214491cbeebacef5acdf13f8f61cc2") } + test("musig2 nonces can only be used once") { + val sk = PrivateKey(ByteVector.fromValidHex("EEC1CB7D1B7254C5CAB0D9C61AB02E643D464A59FE6C96A7EFE871F07C5AEF54")) + val nonce = Musig2.generateNonceWithCounter(0, sk, Seq(sk.publicKey), None, None) + assert(nonce.secretNonce.consume(a => Crypto.sha256(ByteVector.view(a))).isRight) + assert(nonce.secretNonce.consume(a => Crypto.sha256(ByteVector.view(a))).isLeft) + } }