Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
34 changes: 17 additions & 17 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,25 @@

<distributionManagement>
<snapshotRepository>
<id>central-snapshots</id>
<id>central_portal</id>
<url>https://central.sonatype.com/repository/maven-snapshots</url>
</snapshotRepository>
</distributionManagement>

<repositories>
<repository>
<id>central_portal</id>
<name>Sonatype Central Portal (snapshots)</name>
<url>https://central.sonatype.com/repository/maven-snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

<properties>
<project.build.outputTimestamp>2020-01-01T00:00:00Z</project.build.outputTimestamp>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down Expand Up @@ -146,7 +160,7 @@
<dependency>
<groupId>fr.acinq.bitcoin</groupId>
<artifactId>bitcoin-kmp-jvm</artifactId>
<version>0.30.0</version>
<version>0.31.0</version>
</dependency>
<dependency>
<groupId>fr.acinq.secp256k1</groupId>
Expand Down Expand Up @@ -206,7 +220,7 @@
<version>0.8.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<publishingServerId>central_portal</publishingServerId>
<centralSnapshotsUrl>https://central.sonatype.com/repository/maven-snapshots</centralSnapshotsUrl>
</configuration>
</plugin>
Expand All @@ -224,20 +238,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
Expand Down
38 changes: 19 additions & 19 deletions src/main/scala/fr/acinq/bitcoin/scalacompat/Crypto.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)))
}
}

Expand Down Expand Up @@ -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)))

Expand All @@ -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
Expand All @@ -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
Expand All @@ -232,21 +232,21 @@ 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
* @param signature signature
* @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
Expand Down Expand Up @@ -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
Expand All @@ -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))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

/**
Expand All @@ -137,45 +137,45 @@ 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()

/**
*
* @param parent extended private key
* @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))

/**
*
* @param parent extended public key
* @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)

Expand Down
14 changes: 7 additions & 7 deletions src/main/scala/fr/acinq/bitcoin/scalacompat/KotlinUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
10 changes: 6 additions & 4 deletions src/main/scala/fr/acinq/bitcoin/scalacompat/Musig2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}

/**
Expand All @@ -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)
}

/**
Expand All @@ -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)
}

}
Loading
Loading