Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 = 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.

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

private fun getKeyPath(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, _) = getKeyPath(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) = getKeyPath(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