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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ kotlin {
}

all {
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
languageSettings.optIn("kotlin.RequiresOptIn")
}
}

Expand Down
4 changes: 0 additions & 4 deletions src/commonMain/kotlin/fr/acinq/bitcoin/Block.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import kotlin.jvm.JvmStatic
* @param bits The calculated difficulty target being used for this block
* @param nonce The nonce used to generate this block… to allow variations of the header and compute different hashes
*/
@OptIn(ExperimentalUnsignedTypes::class)
public data class BlockHeader(
@JvmField val version: Long,
@JvmField val hashPreviousBlock: ByteVector32,
Expand Down Expand Up @@ -103,7 +102,6 @@ public data class BlockHeader(
}

@JvmStatic
@OptIn(ExperimentalUnsignedTypes::class)
public fun getDifficulty(header: BlockHeader): UInt256 {
val (diff, neg, _) = UInt256.decodeCompact(header.bits)
return if (neg) -diff else diff
Expand All @@ -116,7 +114,6 @@ public data class BlockHeader(
* by bitcoin core
*/
@JvmStatic
@OptIn(ExperimentalUnsignedTypes::class)
public fun blockProof(bits: Long): UInt256 {
val (target, negative, overflow) = UInt256.decodeCompact(bits)
return if (target == UInt256.Zero || negative || overflow) UInt256.Zero else {
Expand Down Expand Up @@ -186,7 +183,6 @@ public object MerkleTree {
}
}

@OptIn(ExperimentalUnsignedTypes::class, ExperimentalStdlibApi::class)
public data class Block(@JvmField val header: BlockHeader, @JvmField val tx: List<Transaction>) {
@JvmField
val hash: ByteVector32 = header.hash
Expand Down
2 changes: 0 additions & 2 deletions src/commonMain/kotlin/fr/acinq/bitcoin/BtcSerializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import fr.acinq.bitcoin.io.Output
import fr.acinq.secp256k1.Hex
import kotlin.jvm.JvmStatic

@OptIn(ExperimentalUnsignedTypes::class)
public abstract class BtcSerializer<T> {
/**
* write a message to a stream
Expand Down Expand Up @@ -196,7 +195,6 @@ public abstract class BtcSerializer<T> {
}

@JvmStatic
@OptIn(ExperimentalStdlibApi::class)
public fun writeVarstring(input: String, out: Output) {
writeVarint(input.length, out)
writeBytes(input.encodeToByteArray(), out)
Expand Down
93 changes: 93 additions & 0 deletions src/commonMain/kotlin/fr/acinq/bitcoin/Descriptor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package fr.acinq.bitcoin

import fr.acinq.bitcoin.DeterministicWallet.derivePrivateKey
import fr.acinq.bitcoin.DeterministicWallet.publicKey
import kotlin.jvm.JvmStatic

public object Descriptor {
private fun polyMod(cc: Long, value: Int): Long {
var c = cc
val c0 = c shr 35
c = ((c and 0x7ffffffffL) shl 5) xor value.toLong()
if ((c0 and 1L) != 0L) c = c xor 0xf5dee51989L
if ((c0 and 2L) != 0L) c = c xor 0xa9fdca3312L
if ((c0 and 4L) != 0L) c = c xor 0x1bab10e32dL
if ((c0 and 8L) != 0L) c = c xor 0x3706b1677aL
if ((c0 and 16L) != 0L) c = c xor 0x644d626ffdL
return c
}

@JvmStatic
public fun checksum(span: String): String {
/** A character set designed such that:
* - The most common 'unprotected' descriptor characters (hex, keypaths) are in the first group of 32.
* - Case errors cause an offset that's a multiple of 32.
* - As many alphabetic characters are in the same group (while following the above restrictions).
*
* If p(x) gives the position of a character c in this character set, every group of 3 characters
* (a,b,c) is encoded as the 4 symbols (p(a) & 31, p(b) & 31, p(c) & 31, (p(a) / 32) + 3 * (p(b) / 32) + 9 * (p(c) / 32).
* This means that changes that only affect the lower 5 bits of the position, or only the higher 2 bits, will just
* affect a single symbol.
*
* As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect
* the position within the groups.
*/
val INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}" + "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~" + "ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "

/** The character set for the checksum itself (same as bech32). */
val CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

var c = 1L
var cls = 0
var clscount = 0
span.forEach { ch ->
val pos = INPUT_CHARSET.indexOf(ch);
if (pos == -1) return "";
c = polyMod(c, pos and 31); // Emit a symbol for the position inside the group, for every character.
cls = cls * 3 + (pos shr 5); // Accumulate the group numbers
clscount += 1
if (clscount == 3) {
// Emit an extra symbol representing the group numbers, for every 3 characters.
c = polyMod(c, cls)
cls = 0
clscount = 0
}
}
if (clscount > 0) c = polyMod(c, cls)
for (j in 0 until 8) c = polyMod(c, 0) // Shift further to determine the checksum.
c = c xor 1 // Prevent appending zeroes from not affecting the checksum.

val ret = StringBuilder(" ")
for (j in 0 until 8) {
val pos1 = (c shr (5 * (7 - j))) and 31
val char = CHECKSUM_CHARSET[pos1.toInt()]
ret[j] = char
}
return ret.toString()
}

private fun getBIP84KeyPath(chainHash: ByteVector32): Pair<String, Int> = when (chainHash) {
Block.RegtestGenesisBlock.hash, Block.TestnetGenesisBlock.hash -> "84'/1'/0'/0" to DeterministicWallet.tpub
Block.LivenetGenesisBlock.hash -> "84'/0'/0'/0" to DeterministicWallet.xpub
else -> error("invalid chain hash $chainHash")
}

@JvmStatic
public fun BIP84Descriptors(chainHash: ByteVector32, master: DeterministicWallet.ExtendedPrivateKey): Pair<String, String> {
val (keyPath, _) = getBIP84KeyPath(chainHash)
val accountPub = publicKey(derivePrivateKey(master, KeyPath(keyPath)))
val fingerprint = DeterministicWallet.fingerprint(master) and 0xFFFFFFFFL
return BIP84Descriptors(chainHash, fingerprint, accountPub)
}

@JvmStatic
public fun BIP84Descriptors(chainHash: ByteVector32, fingerprint: Long, accountPub: DeterministicWallet.ExtendedPublicKey): Pair<String, String> {
val (keyPath, prefix) = getBIP84KeyPath(chainHash)
val accountDesc = "wpkh([${fingerprint.toString(16)}/$keyPath]${DeterministicWallet.encode(accountPub, prefix)}/0/*)"
val changeDesc = "wpkh([${fingerprint.toString(16)}/$keyPath]${DeterministicWallet.encode(accountPub, prefix)}/1/*)"
return Pair(
"$accountDesc#${checksum(accountDesc)}",
"$changeDesc#${checksum(changeDesc)}"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import kotlin.jvm.JvmStatic
/**
* see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
*/
@OptIn(ExperimentalUnsignedTypes::class)
public object DeterministicWallet {
public const val hardenedKeyIndex: Long = 0x80000000L

Expand Down
1 change: 0 additions & 1 deletion src/commonMain/kotlin/fr/acinq/bitcoin/Script.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import kotlin.jvm.JvmStatic

public typealias RunnerCallback = (List<ScriptElt>, List<ByteVector>, Script.Runner.Companion.State) -> Boolean

@OptIn(ExperimentalUnsignedTypes::class)
public object Script {
public const val MaxScriptElementSize: Int = 520
public const val LockTimeThreshold: Long = 500000000L
Expand Down
4 changes: 0 additions & 4 deletions src/commonMain/kotlin/fr/acinq/bitcoin/Transaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import kotlin.jvm.JvmStatic
* @param hash reversed sha256(sha256(tx)) where tx is the transaction we want to refer to
* @param index index of the output in tx that we want to refer to
*/
@OptIn(ExperimentalUnsignedTypes::class)
public data class OutPoint(@JvmField val hash: ByteVector32, @JvmField val index: Long) : BtcSerializable<OutPoint> {
public constructor(hash: ByteArray, index: Long) : this(hash.byteVector32(), index)

Expand Down Expand Up @@ -129,7 +128,6 @@ public data class ScriptWitness(@JvmField val stack: List<ByteVector>) : BtcSeri
* information is updated before inclusion into a block. Repurposed for OP_CSV (see BIPs 68 & 112)
* @param witness Transaction witness (i.e. what is in sig script for standard transactions).
*/
@OptIn(ExperimentalUnsignedTypes::class)
public data class TxIn(
@JvmField val outPoint: OutPoint,
@JvmField val signatureScript: ByteVector,
Expand Down Expand Up @@ -212,7 +210,6 @@ public data class TxIn(
override fun serializer(): BtcSerializer<TxIn> = TxIn
}

@OptIn(ExperimentalUnsignedTypes::class)
public data class TxOut(@JvmField val amount: Satoshi, @JvmField val publicKeyScript: ByteVector) : BtcSerializable<TxOut> {

public constructor(amount: Satoshi, publicKeyScript: ByteArray) : this(amount, publicKeyScript.byteVector())
Expand Down Expand Up @@ -259,7 +256,6 @@ public data class TxOut(@JvmField val amount: Satoshi, @JvmField val publicKeySc
override fun serializer(): BtcSerializer<TxOut> = TxOut
}

@OptIn(ExperimentalUnsignedTypes::class)
public data class Transaction(
@JvmField val version: Long,
@JvmField val txIn: List<TxIn>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class ByteArrayOutput : Output {
private var array: ByteArray = ByteArray(32)
private var position: Int = 0

@OptIn(ExperimentalStdlibApi::class)
private fun ensureCapacity(elementsToAppend: Int) {
if (position + elementsToAppend <= array.size) return
val newArray = ByteArray((position + elementsToAppend).takeHighestOneBit() shl 1)
Expand Down
1 change: 0 additions & 1 deletion src/commonMain/kotlin/fr/acinq/bitcoin/psbt/Psbt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import kotlin.jvm.JvmStatic
* @param inputs signing data for each input of the transaction to be signed (order matches the unsigned tx).
* @param outputs signing data for each output of the transaction to be signed (order matches the unsigned tx).
*/
@OptIn(ExperimentalUnsignedTypes::class)
public data class Psbt(val global: Global, val inputs: List<Input>, val outputs: List<Output>) {

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package fr.acinq.bitcoin
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalStdlibApi::class)
class Base58TestsCommon {
@Test
fun `basic encode-decode tests`() {
Expand Down
34 changes: 34 additions & 0 deletions src/commonTest/kotlin/fr/acinq/bitcoin/DescriptorTestsCommon.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package fr.acinq.bitcoin

import kotlin.test.Test
import kotlin.test.assertEquals

class DescriptorTestsCommon {
@Test
fun `compute descriptor checksums`() {
val data = listOf(
"pkh([6ded4eb8/44h/0h/0h]xpub6C6N5WVF5zmurBR52MZZj8Jxm6eDiKyM4wFCm7xTYBEsAvJPqBKp2u2K7RTsZaYDN8duBWq4acrD4vrwjaKHTYuntGjL334nVHtLNuaj5Mu/0/*)#5mzpq0w6",
"wpkh([6ded4eb8/84h/0h/0h]xpub6CDeom4xT3Wg7BuyXU2Sd9XerTKttyfxRwJE36mi5HxFYpYdtdwM76Zx8swPnc6zxuArMYJgjNy91fJ13YtGPHgf49YqA8KdXg6D69tzNFh/0/*)#refya6f0",
"sh(wpkh([6ded4eb8/49h/0h/0h]xpub6Cb8jR9kYsfC6kj9CsE18SyudWjW2V3FnBFkT2oqq6n7NWWvJrjhFin3sAYg8X7ApX8iPophBa98mo4nMvSxnqrXvpnwaRopecQz859Ai1s/0/*))#xrhyhtvl",
"tr([6ded4eb8/86h/0h/0h]xpub6CDp1iw76taes3pkqfiJ6PYhwURkaYksJ62CrrdTVr6ow9wR9mKAtUGoZQqb8pRDiq2F8k31tYrrJjVGTRSLYGQ7nYpmewH94ThsAgDxJ4h/0/*)#2nm7drky",
"pkh([6ded4eb8/44h/0h/0h]xpub6C6N5WVF5zmurBR52MZZj8Jxm6eDiKyM4wFCm7xTYBEsAvJPqBKp2u2K7RTsZaYDN8duBWq4acrD4vrwjaKHTYuntGjL334nVHtLNuaj5Mu/1/*)#908qa67z",
"wpkh([6ded4eb8/84h/0h/0h]xpub6CDeom4xT3Wg7BuyXU2Sd9XerTKttyfxRwJE36mi5HxFYpYdtdwM76Zx8swPnc6zxuArMYJgjNy91fJ13YtGPHgf49YqA8KdXg6D69tzNFh/1/*)#jdv9q0eh",
"sh(wpkh([6ded4eb8/49h/0h/0h]xpub6Cb8jR9kYsfC6kj9CsE18SyudWjW2V3FnBFkT2oqq6n7NWWvJrjhFin3sAYg8X7ApX8iPophBa98mo4nMvSxnqrXvpnwaRopecQz859Ai1s/1/*))#nzej05eq",
"tr([6ded4eb8/86h/0h/0h]xpub6CDp1iw76taes3pkqfiJ6PYhwURkaYksJ62CrrdTVr6ow9wR9mKAtUGoZQqb8pRDiq2F8k31tYrrJjVGTRSLYGQ7nYpmewH94ThsAgDxJ4h/1/*)#m87lskxu"
)
data.forEach { dnc ->
val (desc, checksum) = dnc.split('#').toTypedArray()
assertEquals(checksum, Descriptor.checksum(desc))
}
}

@Test
fun `compute BIP84 descriptors`() {
val seed = ByteVector.fromHex("817a9c8e6ba36f083d7e68b5ee89ce74fde9ef294a724a5efc5cef2b88db057f")
val master = DeterministicWallet.generate(seed)
val (accountDesc, changeDesc) = Descriptor.BIP84Descriptors(Block.RegtestGenesisBlock.hash, master)
assertEquals("wpkh([189ef5fe/84'/1'/0'/0]tpubDFTu6FhLqfTBLMd7BvGkyH1h4XBw7XoKWfnNNWw5Sp8V6aC55EhgPTVNAYvBwBXQ8EGnMqaZi3dpdSzhMbD4Z7ivZiaVKNMUkXVjDU1CDuE/0/*)#uysr3s9y", accountDesc)
assertEquals("wpkh([189ef5fe/84'/1'/0'/0]tpubDFTu6FhLqfTBLMd7BvGkyH1h4XBw7XoKWfnNNWw5Sp8V6aC55EhgPTVNAYvBwBXQ8EGnMqaZi3dpdSzhMbD4Z7ivZiaVKNMUkXVjDU1CDuE/1/*)#ds4zv94u", changeDesc)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import fr.acinq.secp256k1.Hex
import kotlin.test.Test
import kotlin.test.assertTrue

@OptIn(ExperimentalStdlibApi::class)
class Ripemd160TestsCommon {
@Test // from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
fun `reference tests`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import fr.acinq.secp256k1.Hex
import kotlin.test.Test
import kotlin.test.assertTrue

@OptIn(ExperimentalStdlibApi::class)
class Sha1TestsCommon {
val testVectors = arrayOf(
"" to "da39a3ee5e6b4b0d3255bfef95601890afd80709",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import fr.acinq.secp256k1.Hex
import kotlin.test.Test
import kotlin.test.assertTrue

@OptIn(ExperimentalStdlibApi::class)
class Sha512TestsCommon {
@Test
fun `reference tests`() {
Expand Down