From 65f2c0fcf1786cc1b8f271689f1ec8b718d3c68e Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 20 Oct 2022 11:52:59 -0400 Subject: [PATCH] Bump bdk-ffi submodule to v0.10.0 tag --- api-docs/build.gradle.kts | 4 +- api-docs/settings.gradle.kts | 2 +- .../src/main/kotlin/org/bitcoindevkit/bdk.kt | 92 ++++++++++--- .../test/kotlin/org/bitcoindevkit/Samples.kt | 126 ++++++++++++++++++ bdk-ffi | 2 +- 5 files changed, 200 insertions(+), 26 deletions(-) diff --git a/api-docs/build.gradle.kts b/api-docs/build.gradle.kts index 920ff8e..46a1921 100644 --- a/api-docs/build.gradle.kts +++ b/api-docs/build.gradle.kts @@ -27,7 +27,7 @@ tasks.withType().configureEach { dokkaSourceSets { named("main") { moduleName.set("bdk-android") - moduleVersion.set("0.9.0") + moduleVersion.set("0.10.0") includes.from("Module1.md") samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt") } @@ -38,7 +38,7 @@ tasks.withType().configureEach { // dokkaSourceSets { // named("main") { // moduleName.set("bdk-jvm") -// moduleVersion.set("0.9.0") +// moduleVersion.set("0.10.0") // includes.from("Module2.md") // samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt") // } diff --git a/api-docs/settings.gradle.kts b/api-docs/settings.gradle.kts index eefa331..3b7cdb5 100644 --- a/api-docs/settings.gradle.kts +++ b/api-docs/settings.gradle.kts @@ -1 +1 @@ -rootProject.name = "bdk-kotlin-docs" +rootProject.name = "BDK Android and BDK JVM API Docs" diff --git a/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt b/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt index e444edd..d3bc6ce 100644 --- a/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt +++ b/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt @@ -211,6 +211,15 @@ class PartiallySignedBitcoinTransaction(psbtBase64: String) { /** Get the txid of the PSBT. */ fun txid(): String {} + + /** Return the transaction as bytes. */ + fun `extractTx`(): List + + /** + * Combines this PartiallySignedTransaction with another PSBT as described by BIP 174. + * In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)` + */ + fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction } /** @@ -272,10 +281,6 @@ data class BlockTime ( var timestamp: ULong, ) -/** - - */ - /** * A Bitcoin wallet. * The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are: @@ -289,6 +294,8 @@ data class BlockTime ( * @param changeDescriptor The change (or "internal") descriptor. * @param network The network to act on. * @param databaseConfig The database configuration. + * + * @sample org.bitcoindevkit.walletSample */ class Wallet( descriptor: String, @@ -333,7 +340,7 @@ class Progress { /** * A transaction builder. * - * After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction. + * After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction. * * Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added. */ @@ -342,10 +349,10 @@ class TxBuilder() { fun addData(data: List): TxBuilder {} /** Add a recipient to the internal list. */ - fun addRecipient(address: String, amount: ULong): TxBuilder {} + fun addRecipient(script: Script, amount: ULong): TxBuilder {} /** Set the list of recipients by providing a list of [AddressAmount]. */ - fun setRecipients(recipients: List): TxBuilder {} + fun setRecipients(recipients: List): TxBuilder {} /** Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent" utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details. */ fun addUnspendable(unspendable: OutPoint): TxBuilder {} @@ -408,18 +415,18 @@ class TxBuilder() { */ fun enableRbfWithSequence(nsequence: UInt): TxBuilder {} - /** Finish building the transaction. Returns the BIP174 PSBT. */ - fun finish(wallet: Wallet): PartiallySignedBitcoinTransaction {} + /** Finish building the transaction. Returns a [TxBuilderResult]. */ + fun finish(wallet: Wallet): TxBuilderResult {} } /** - * A object holding an address and an amount. + * A object holding an ScriptPubKey and an amount. * - * @property address The address. + * @property script The ScriptPubKey. * @property amount The amount. */ data class AddressAmount ( - var address: String, + var script: Script, var amount: ULong ) @@ -446,8 +453,8 @@ class BumpFeeTxBuilder() { */ fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {} - /** Finish building the transaction. Returns the BIP174 PSBT. */ - fun finish(wallet: Wallet): PartiallySignedBitcoinTransaction {} + /** Finish building the transaction. Returns a [TxBuilderResult]. */ + fun finish(wallet: Wallet): TxBuilderResult {} } /** @@ -455,17 +462,28 @@ class BumpFeeTxBuilder() { * * @param wordCount The number of words to use for the mnemonic (also determines the amount of entropy that is used). * @return The mnemonic words separated by a space in a String + * + * @sample org.bitcoindevkit.generateMnemonicSample */ fun generateMnemonic(wordCount: WordCount): String /** - * TODO + * A BIP-32 derivation path. * - * @constructor TODO + * @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey] + * or [DescriptorPublicKey]. + */ +class DerivationPath(path: String) {} + +/** + * An extended secret key. * * @param network The network this DescriptorSecretKey is to be used on. * @param mnemonic The mnemonic. * @param password The optional passphrase that can be provided as per BIP-39. + * + * @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample + * @sample org.bitcoindevkit.descriptorSecretKeyExtendSample */ class DescriptorSecretKey(network: Network, mnemonic: String, password: String?) { /** Derive a private descriptor at a given path. */ @@ -477,14 +495,15 @@ class DescriptorSecretKey(network: Network, mnemonic: String, password: String?) /** Return the public version of the descriptor. */ fun asPublic(): DescriptorPublicKey {} + /* Return the raw private key as bytes. */ + fun secretBytes(): List + /** Return the private descriptor as a string. */ fun asString(): String {} } /** - * TODO - * - * @constructor TODO + * An extended public key. * * @param network The network this DescriptorPublicKey is to be used on. * @param mnemonic The mnemonic. @@ -492,13 +511,13 @@ class DescriptorSecretKey(network: Network, mnemonic: String, password: String?) */ class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) { /** Derive a public descriptor at a given path. */ - fun derive(path: DerivationPath): DescriptorSecretKey {} + fun derive(path: DerivationPath): DescriptorSecretKey /** Extend the public descriptor with a custom path. */ - fun extend(path: DerivationPath): DescriptorSecretKey {} + fun extend(path: DerivationPath): DescriptorSecretKey /** Return the public descriptor as a string. */ - fun asString(): String {} + fun asString(): String } /** @@ -520,3 +539,32 @@ enum class WordCount { /** 24 words mnemonic (256 bits entropy). */ WORDS24, } + +/** + * The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder]. + * + * @property psbt The PSBT + * @property transactionDetails The transaction details. + * + * @sample org.bitcoindevkit.txBuilderResultSample1 + * @sample org.bitcoindevkit.txBuilderResultSample2 + */ +data class TxBuilderResult ( + var psbt: PartiallySignedBitcoinTransaction, + var transactionDetails: TransactionDetails +) + +/** + * A bitcoin script. + */ +class Script(rawOutputScript: List) + +/** + * A bitcoin address. + * + * @param address The address in string format. + */ +class Address(address: String) { + /* Return the ScriptPubKey. */ + fun scriptPubkey(): Script +} diff --git a/api-docs/src/test/kotlin/org/bitcoindevkit/Samples.kt b/api-docs/src/test/kotlin/org/bitcoindevkit/Samples.kt index 36e7c33..198e896 100644 --- a/api-docs/src/test/kotlin/org/bitcoindevkit/Samples.kt +++ b/api-docs/src/test/kotlin/org/bitcoindevkit/Samples.kt @@ -98,3 +98,129 @@ fun blockchainSample() { blockchain.broadcast(signedPsbt) } + +fun txBuilderResultSample1() { + val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") + // TxBuilderResult is a data class, which means you can use destructuring declarations on it to + // open it up in its component parts + val (psbt, txDetails) = TxBuilder() + .addRecipient(faucetAddress.scriptPubkey(), 1000u) + .feeRate(1.2f) + .finish(wallet) + + println("Txid is ${txDetails.txid}") + wallet.sign(psbt) +} + +fun txBuilderResultSample2() { + val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") + val txBuilderResult: TxBuilderResult = TxBuilder() + .addRecipient(faucetAddress.scriptPubkey(), 1000u) + .feeRate(1.2f) + .finish(wallet) + + val psbt = txBuilderResult.psbt + val txDetails = txBuilderResult.transactionDetails + + println("Txid is ${txDetails.txid}") + wallet.sign(psbt) +} + +fun descriptorSecretKeyExtendSample() { + // The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path. + + // val mnemonic: String = generateMnemonic(WordCount.WORDS12) + val mnemonic: String = "scene change clap smart together mind wheel knee clip normal trial unusual" + + // the initial DescriptorSecretKey will always be at the "master" node, + // i.e. the derivation path is empty + val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey( + network = Network.TESTNET, + mnemonic = mnemonic, + password = "" + ) + println(bip32RootKey.asString()) + // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/* + + // the derive method will also automatically apply the wildcard (*) to your path, + // i.e the following will generate the typical testnet BIP84 external wallet path + // m/84h/1h/0h/0/* + val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0") + val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString() + println(externalExtendedKey) + // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/* + + // to create the descriptor you'll need to use this extended key in a descriptor function, + // i.e. wpkh(), tr(), etc. + val externalDescriptor = "wpkh($externalExtendedKey)" +} + +fun descriptorSecretKeyDeriveSample() { + // The DescriptorSecretKey.derive() method allows you to derive an extended key for a given + // node in the derivation tree (for example to create an xpub for a particular account) + + val mnemonic: String = "scene change clap smart together mind wheel knee clip normal trial unusual" + val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey( + network = Network.TESTNET, + mnemonic = mnemonic, + password = "" + ) + + val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h") + val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0) + println(xpubAccount0.asString()) + // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/* + + val internalPath: DerivationPath = DerivationPath("m/0") + val externalExtendedKey = xpubAccount0.extend(internalPath).asString() + println(externalExtendedKey) + // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/* + + // to create the descriptor you'll need to use this extended key in a descriptor function, + // i.e. wpkh(), tr(), etc. + val externalDescriptor = "wpkh($externalExtendedKey)" +} + +fun createTransaction() { + val wallet = BdkWallet( + descriptor = externalDescriptor, + changeDescriptor = internalDescriptor, + network = Network.TESTNET, + databaseConfig = memoryDatabaseConfig, + ) + val blockchainConfig = BlockchainConfig.Electrum( + ElectrumConfig( + "ssl://electrum.blockstream.info:60002", + null, + 5u, + null, + 200u + ) + ) + + val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") + val (psbt, txDetails) = TxBuilder() + .addRecipient(faucetAddress.scriptPubkey(), 1000u) + .feeRate(1.2f) + .finish(wallet) + + wallet.sign(psbt) + blockchain.broadcast(psbt) +} + +fun walletSample() { + val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)" + val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)" + val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite")) + + val wallet = BdkWallet( + descriptor = externalDescriptor, + changeDescriptor = internalDescriptor, + network = Network.TESTNET, + databaseConfig = sqliteDatabaseConfig, + ) +} + +fun generateMnemonicSample() { + val mnemonic: String = generateMnemonic(WordCount.WORDS12) +} \ No newline at end of file diff --git a/bdk-ffi b/bdk-ffi index 485f4f7..c7d0803 160000 --- a/bdk-ffi +++ b/bdk-ffi @@ -1 +1 @@ -Subproject commit 485f4f72ce4d625e988effa67701adab0039aa64 +Subproject commit c7d0803000d31d8a6d731287ed5a9ab21d39828b