diff --git a/.gitignore b/.gitignore index 277a164..2e06997 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ xcuserdata .lsp .clj-kondo .idea -bdk.kt +android/src/main/kotlin/org/bitcoindevkit/bdk.kt +jvm/src/main/kotlin/org/bitcoindevkit/bdk.kt diff --git a/api-docs/Module.md b/api-docs/Module.md index b7efdc2..345f22f 100644 --- a/api-docs/Module.md +++ b/api-docs/Module.md @@ -1,5 +1,5 @@ # Module bdk-android -The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android and the JVM. +The [bitcoindevkit](https://bitcoindevkit.org/) language bindings libraries for Android and the JVM. These docs are valid for both `bdk-jvm` and `bdk-android` libraries. diff --git a/api-docs/build.gradle.kts b/api-docs/build.gradle.kts index d9b586d..9394b59 100644 --- a/api-docs/build.gradle.kts +++ b/api-docs/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.7.0" + kotlin("jvm") version "1.7.10" // API docs id("org.jetbrains.dokka") version "1.7.10" @@ -27,7 +27,7 @@ tasks.withType().configureEach { dokkaSourceSets { named("main") { moduleName.set("bdk-android") - moduleVersion.set("0.8.2") + moduleVersion.set("0.9.0") includes.from("Module.md") samples.from("src/main/kotlin/org/bitcoindevkit/Samples.kt") } diff --git a/api-docs/src/main/kotlin/org/bitcoindevkit/Samples.kt b/api-docs/src/main/kotlin/org/bitcoindevkit/Samples.kt index 4e355fd..012d5cd 100644 --- a/api-docs/src/main/kotlin/org/bitcoindevkit/Samples.kt +++ b/api-docs/src/main/kotlin/org/bitcoindevkit/Samples.kt @@ -11,4 +11,3 @@ class Samples { ) ) } - diff --git a/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt b/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt new file mode 100644 index 0000000..8cf2cb9 --- /dev/null +++ b/api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt @@ -0,0 +1,427 @@ +package org.bitcoindevkit + +/** + * The cryptocurrency to act on. + */ +enum class Network { + /** Bitcoin's mainnet */ + BITCOIN, + + /** Bitcoin’s testnet */ + TESTNET, + + /** Bitcoin’s signet */ + SIGNET, + + /** Bitcoin’s regtest */ + REGTEST, +} + +/** + * A derived address and the index it was found at. + */ +data class AddressInfo ( + /** Child index of this address. */ + var index: UInt, + + /** Address. */ + var address: String +) + +/** + * The address index selection strategy to use to derive an address from the wallet’s external descriptor. + * + * If you’re unsure which one to use, use `AddressIndex.NEW`. + */ +enum class AddressIndex { + /** Return a new address after incrementing the current descriptor index. */ + NEW, + + /** + * Return the address for the current descriptor index if it has not been used in a received transaction. Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet has not yet detected an address has been used it could return an already used address. This function is primarily meant for situations where the caller is untrusted; for example when deriving donation addresses on-demand for a public web page. + */ + LAST_UNUSED, +} + +/** + * Type that can contain any of the database configurations defined by the library. + */ +sealed class DatabaseConfig { + /** Configuration for an in-memory database */ + object Memory : DatabaseConfig() + + /** Configuration for a Sled database */ + data class Sled(val config: SledDbConfiguration) : DatabaseConfig() + + /** Configuration for a SQLite database */ + data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig() +} + +/** + * Configuration type for a SQLite database. + */ +data class SqliteDbConfiguration( + /** Main directory of the db */ + var path: String, +) + +/** + * Configuration type for a SledDB database. + */ +data class SledDbConfiguration( + /** Main directory of the db */ + var path: String, + + /** Name of the database tree, a separated namespace for the data */ + var treeName: String, +) + +/** + * Configuration for an Electrum blockchain. + * + * @sample org.bitcoindevkit.Samples.blockchainConfig + */ +data class ElectrumConfig ( + /** URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`. */ + var url: String, + + /** URL of the socks5 proxy server or a Tor service. */ + var socks5: String?, + + /** Request retry count. */ + var retry: UByte, + + /** Request timeout (seconds). */ + var timeout: UByte?, + + /** Stop searching addresses for transactions after finding an unused gap of this length. */ + var stopGap: ULong +) + +/** + * Configuration for an Esplora blockchain. + */ +data class EsploraConfig ( + /** Base URL of the esplora service, e.g. `https://blockstream.info/api/`. */ + var baseUrl: String, + + /** Optional URL of the proxy to use to make requests to the Esplora server. */ + var proxy: String?, + + /** Number of parallel requests sent to the esplora service (default: 4). */ + var concurrency: UByte?, + + /** Stop searching addresses for transactions after finding an unused gap of this length. */ + var stopGap: ULong, + + /** Socket timeout. */ + var timeout: ULong? +) + +/** + * Type that can contain any of the blockchain configurations defined by the library. + * + * @sample org.bitcoindevkit.Samples.blockchainConfig + */ +sealed class BlockchainConfig { + /** Electrum client */ + data class Electrum(val config: ElectrumConfig) : BlockchainConfig() + + /** Esplora client */ + data class Esplora(val config: EsploraConfig) : BlockchainConfig() +} + +/** + * A wallet transaction. + */ +data class TransactionDetails ( + /** Fee value (sats) if available. The availability of the fee depends on the backend. It’s never None with an Electrum server backend, but it could be None with a Bitcoin RPC node without txindex that receive funds while offline. */ + var fee: ULong?, + + /** Received value (sats) Sum of owned outputs of this transaction. */ + var received: ULong, + + /** Sent value (sats) Sum of owned inputs of this transaction. */ + var sent: ULong, + + /** Transaction id. */ + var txid: String + + /** If the transaction is confirmed, [BlockTime] contains height and timestamp of the block containing the transaction. This property is null for unconfirmed transactions. */ + var confirmationTime: BlockTime? +) + +/** + * A blockchain backend. + */ +class Blockchain( + config: BlockchainConfig +) { + /** Broadcast a transaction. */ + fun broadcast(psbt: PartiallySignedBitcoinTransaction): String {} + + /** Get the current height of the blockchain. */ + fun getHeight(): UInt {} + + /** Get the block hash of a given block. */ + fun getBlockHash(height: UInt): String {} +} + +/** + * A partially signed bitcoin transaction. + */ +class PartiallySignedBitcoinTransaction(psbtBase64: String) { + /** Return the PSBT in string format, using a base64 encoding. */ + fun serialize(): String {} + + /** Get the txid of the PSBT. */ + fun txid(): String {} +} + +/** + * A reference to a transaction output. + */ +data class OutPoint ( + /** The referenced transaction’s txid. */ + var txid: String, + + /** The index of the referenced output in its transaction’s vout. */ + var vout: UInt +) + +/** + * A transaction output, which defines new coins to be created from old ones. + */ +data class TxOut ( + /** The value of the output, in satoshis. */ + var value: ULong, + + /** The address of the output. */ + var address: String +) + +/** + * An unspent output owned by a [Wallet]. + */ +data class LocalUtxo ( + /** Reference to a transaction output. */ + var outpoint: OutPoint, + + /** Transaction output. */ + var txout: TxOut, + + /** Type of keychain. */ + var keychain: KeychainKind, + + /** Whether this UTXO is spent or not. */ + var isSpent: Boolean +) + +/** + * Types of keychains. + */ +enum class KeychainKind { + /** External */ + EXTERNAL, + + /** Internal, usually used for change outputs. */ + INTERNAL, +} + +/** + * Block height and timestamp of a block. + */ +data class BlockTime ( + /** confirmation block height */ + var height: UInt, + + /** confirmation block timestamp */ + 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: + * 1. Output descriptors from which it can derive addresses. + * 2. A Database where it tracks transactions and utxos related to the descriptors. + * 3. Signers that can contribute signatures to addresses instantiated from the descriptors. + */ +class Wallet( + descriptor: String, + changeDescriptor: String, + network: Network, + databaseConfig: DatabaseConfig, +) { + /** Return a derived address using the external descriptor, see [AddressIndex] for available address index selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character) then the same address will always be returned for any [AddressIndex]. */ + fun getAddress(addressIndex: AddressIndex): AddressInfo {} + + /** Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */ + fun getBalance(): ULong {} + + /** Sign a transaction with all the wallet’s signers. */ + fun sign(psbt: PartiallySignedBitcoinTransaction): Boolean {} + + /** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */ + fun listTransactions(): List {} + + /** Get the Bitcoin network the wallet is using. */ + fun network(): Network {} + + /** Sync the internal database with the blockchain. */ + fun sync(blockchain: Blockchain, progress: Progress?) {} + + /** Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */ + fun listUnspent(): List {} +} + +/** + * Class that logs at level INFO every update received (if any). + */ +class Progress { + /** Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an optional text message that can be displayed to the user. */ + fun update(progress: Float, message: String?) {} +} + +/** + * A transaction builder. + * + * 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. + */ +class TxBuilder() { + /** Add data as an output using OP_RETURN. */ + fun addData(data: List): TxBuilder {} + + /** Add a recipient to the internal list. */ + fun addRecipient(address: String, amount: ULong): TxBuilder {} + + /** Set the list of recipients by providing a list of [AddressAmount]. */ + 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 {} + + /** Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent. */ + fun addUtxo(outpoint: OutPoint): TxBuilder {} + + /** Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding any of the UTXOs then none of them are added and the error is returned. These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent. */ + fun addUtxos(outpoints: List): TxBuilder {} + + /** Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */ + fun doNotSpendChange(): TxBuilder {} + + /** Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid. */ + fun manuallySelectedOnly(): TxBuilder {} + + /** Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */ + fun onlySpendChange(): TxBuilder {} + + /** Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details. */ + fun unspendable(unspendable: List): TxBuilder {} + + /** Set a custom fee rate. */ + fun feeRate(satPerVbyte: Float): TxBuilder {} + + /** Set an absolute fee. */ + fun feeAbsolute(feeAmount: ULong): TxBuilder {} + + /** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */ + fun drainWallet(): TxBuilder {} + + /** Sets the address to drain excess coins to. Usually, when there are excess coins they are sent to a change address generated by the wallet. This option replaces the usual change address with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the drain output is not needed (the excess coins are too small) it will not be included in the resulting transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option, you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees. */ + fun drainTo(address: String): TxBuilder {} + + /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */ + fun enableRbf(): TxBuilder {} + + /** Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF. */ + fun enableRbfWithSequence(nsequence: UInt): TxBuilder {} + + /** Finish building the transaction. Returns the BIP174 PSBT. */ + fun finish(wallet: Wallet): PartiallySignedBitcoinTransaction {} +} + +/** + * A object holding an address and an amount. + */ +data class AddressAmount ( + /** The address. */ + var address: String, + + /** The amount. */ + var amount: ULong +) {} + +/** + * The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true. + */ +class BumpFeeTxBuilder() { + /** Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey can’t be found among the recipients of the transaction we are bumping. */ + fun allowShrinking(address: String): BumpFeeTxBuilder {} + + /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */ + fun enableRbf(): BumpFeeTxBuilder {} + + /** Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF. */ + fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {} + + /** Finish building the transaction. Returns the BIP174 PSBT. */ + fun finish(wallet: Wallet): PartiallySignedBitcoinTransaction {} +} + +/** + * Generates a new mnemonic using the English word list and the given number of words (12, 15, 18, 21, or 24). + */ +fun generateMnemonic(wordCount: WordCount): String {} + +/** + * + */ +class DescriptorSecretKey(network: Network, mnemonic: String, password: String?) { + /** Derive a private descriptor at a given path. */ + fun derive(path: DerivationPath): DescriptorSecretKey {} + + /** Extend the private descriptor with a custom path. */ + fun extend(path: DerivationPath): DescriptorSecretKey {} + + /** Return the public version of the descriptor. */ + fun asPublic(): DescriptorPublicKey {} + + /** Return the private descriptor as a string. */ + fun asString(): String {} +} + +/** + * + */ +class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) { + /** Derive a public descriptor at a given path. */ + fun derive(path: DerivationPath): DescriptorSecretKey {} + + /** Extend the public descriptor with a custom path. */ + fun extend(path: DerivationPath): DescriptorSecretKey {} + + /** Return the public descriptor as a string. */ + fun asString(): String {} +} + +/** + * An enum describing entropy length (aka word count) in the mnemonic. + */ +enum class WordCount { + /** 12 words mnemonic (128 bits entropy) */ + WORDS12, + + /** 15 words mnemonic (160 bits entropy) */ + WORDS15, + + /** 18 words mnemonic (192 bits entropy) */ + WORDS18, + + /** 21 words mnemonic (224 bits entropy) */ + WORDS21, + + /** 24 words mnemonic (256 bits entropy) */ + WORDS24, +}