├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── pull_request_template.md └── workflows │ ├── publish-android.yaml │ ├── publish-jvm.yaml │ ├── test-android.yaml │ └── test-jvm.yaml ├── .gitignore ├── .gitmodules ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── PGP-BDK-BINDINGS.asc ├── README.md ├── api-docs ├── Module1.md ├── Module2.md ├── build.gradle.kts ├── deploy.sh ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── org │ │ └── bitcoindevkit │ │ └── bdk.kt │ └── test │ └── kotlin │ └── org │ └── bitcoindevkit │ └── Samples.kt ├── bdk-android ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ ├── assets │ │ │ └── logback.xml │ │ └── kotlin │ │ │ └── org │ │ │ └── bitcoindevkit │ │ │ └── AndroidLibTest.kt │ │ └── main │ │ └── AndroidManifest.xml ├── plugins │ ├── README.md │ ├── build.gradle.kts │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── bitcoindevkit │ │ └── plugins │ │ ├── Enums.kt │ │ └── UniFfiAndroidPlugin.kt └── settings.gradle.kts └── bdk-jvm ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── build.gradle.kts └── src │ └── test │ └── kotlin │ └── org │ └── bitcoindevkit │ └── JvmLibTest.kt ├── plugins ├── README.md ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── org │ └── bitcoindevkit │ └── plugins │ ├── Enums.kt │ └── UniFfiJvmPlugin.kt └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.rs] 15 | indent_size = 4 16 | 17 | [*.kt] 18 | indent_size = 4 19 | 20 | [*.gradle] 21 | indent_size = 4 22 | 23 | [tests/**/*.rs] 24 | charset = utf-8 25 | end_of_line = unset 26 | indent_size = unset 27 | indent_style = unset 28 | trim_trailing_whitespace = unset 29 | insert_final_newline = unset 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | labels: [ "bug", "triage" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: Description 13 | description: A concise description of the bug, what happened? 14 | placeholder: What did you see... 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: reproduce 19 | attributes: 20 | label: How to Reproduce 21 | description: Steps or code to reproduce the behavior. 22 | placeholder: How can we reproduce it... 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: expected 27 | attributes: 28 | label: Expected Result 29 | description: What did you expected to happen. 30 | placeholder: Tell us what you were expecting... 31 | validations: 32 | required: true 33 | - type: textarea 34 | id: version 35 | attributes: 36 | label: Version 37 | description: Which release version(s), commit, or branch of code do you see this bug? 38 | placeholder: Tell us which version(s) of code you saw the bug in... 39 | validations: 40 | required: true 41 | - type: checkboxes 42 | id: artifact 43 | attributes: 44 | label: Artifact 45 | description: With which artifact(s) are you seeing this bug? 46 | options: 47 | - label: bdk-jvm 48 | - label: bdk-android 49 | - type: checkboxes 50 | id: platform 51 | attributes: 52 | label: Platform 53 | description: What target platform(s) are you seeing the problem on? 54 | options: 55 | - label: ARM64 Android 56 | - label: 64-bit x86 Android 57 | - label: 32-bit x86 Android 58 | - label: 64-bit x86 Linux (kernel 2.6.32+, glibc 2.11+) 59 | - label: 64-bit x86 macOS (10.7+, Lion+) 60 | - label: ARM64 macOS (11.0+, Big Sur+) 61 | - type: textarea 62 | id: logs 63 | attributes: 64 | label: Relevant log output 65 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 66 | render: shell 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 📝 Official Documentation 4 | url: https://bitcoindevkit.org/getting-started/ 5 | about: Check our documentation for answers to common questions 6 | - name: 💬 Community Chat 7 | url: https://discord.com/invite/dstn4dQ 8 | about: Ask general questions and get community support in real-time 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a new feature 3 | labels: [ "enhancement", "triage" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to request and document this new feature! 9 | - type: textarea 10 | id: problem 11 | attributes: 12 | label: Problem 13 | description: A concise description of the problem you need this new feature to solve. 14 | placeholder: What is the problem you are trying to solve... 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: description 19 | attributes: 20 | label: Description 21 | description: A concise description of the new feature you need. 22 | placeholder: What will this new feature do, how will it work... 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: alternatives 27 | attributes: 28 | label: Alternatives 29 | description: Describe any other alternatives you considered. 30 | placeholder: Other ways you considered to solve your problem... 31 | validations: 32 | required: false 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Description 4 | 5 | 6 | 7 | ### Notes to the reviewers 8 | 9 | 11 | 12 | ### Checklists 13 | 14 | #### All Submissions: 15 | 16 | * [ ] I've signed all my commits 17 | * [ ] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) 18 | * [ ] I ran `cargo fmt` and `cargo clippy` before committing 19 | 20 | #### New Features: 21 | 22 | * [ ] I've added tests for the new feature 23 | * [ ] I've added docs for the new feature 24 | * [ ] I've updated `CHANGELOG.md` 25 | 26 | #### Bugfixes: 27 | 28 | * [ ] This pull request breaks the existing API 29 | * [ ] I've added tests to reproduce the issue which are now passing 30 | * [ ] I'm linking the issue being fixed by this PR 31 | -------------------------------------------------------------------------------- /.github/workflows/publish-android.yaml: -------------------------------------------------------------------------------- 1 | name: Publish bdk-android to Maven Central 2 | on: [workflow_dispatch] 3 | 4 | env: 5 | ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529 6 | # By default the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT 7 | # ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Install Android NDK 21.4.7075529 14 | run: | 15 | ANDROID_ROOT=/usr/local/lib/android 16 | ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk 17 | SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager 18 | echo "y" | $SDKMANAGER "ndk;21.4.7075529" 19 | 20 | - name: Check out PR branch 21 | uses: actions/checkout@v2 22 | 23 | - name: Update bdk-ffi git submodule 24 | run: | 25 | git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git 26 | git submodule update --init bdk-ffi 27 | 28 | - name: cache 29 | uses: actions/cache@v2 30 | with: 31 | path: | 32 | ~/.cargo/registry 33 | ~/.cargo/git 34 | bdk-ffi/target 35 | key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} 36 | 37 | - name: Set up JDK 38 | uses: actions/setup-java@v2 39 | with: 40 | distribution: temurin 41 | java-version: 11 42 | 43 | - name: Install rust android targets 44 | run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi 45 | 46 | - name: Build bdk-android library 47 | run: | 48 | cd bdk-android 49 | ./gradlew buildAndroidLib 50 | 51 | - name: Publish to Maven Local and Maven Central 52 | env: 53 | ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }} 54 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }} 55 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }} 56 | ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }} 57 | ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }} 58 | run: | 59 | cd bdk-android 60 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository 61 | -------------------------------------------------------------------------------- /.github/workflows/publish-jvm.yaml: -------------------------------------------------------------------------------- 1 | name: Publish bdk-jvm to Maven Central 2 | on: [workflow_dispatch] 3 | #on: 4 | # release: 5 | # types: [published] 6 | 7 | jobs: 8 | build-jvm-macOS-M1-native-lib: 9 | name: Create M1 and x86_64 JVM native binaries 10 | runs-on: macos-12 11 | steps: 12 | - name: Checkout publishing branch 13 | uses: actions/checkout@v2 14 | 15 | - name: Update bdk-ffi git submodule 16 | run: | 17 | git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git 18 | git submodule update --init bdk-ffi 19 | 20 | - name: Cache 21 | uses: actions/cache@v3 22 | with: 23 | path: | 24 | ~/.cargo/registry 25 | ~/.cargo/git 26 | ./bdk-ffi/target 27 | key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} 28 | 29 | - name: Set up JDK 30 | uses: actions/setup-java@v2 31 | with: 32 | distribution: temurin 33 | java-version: 11 34 | 35 | - name: Install aarch64 Rust target 36 | run: rustup target add aarch64-apple-darwin 37 | 38 | - name: Build bdk-jvm library 39 | run: | 40 | cd bdk-jvm 41 | ./gradlew buildJvmLib 42 | 43 | # build aarch64 + x86_64 native libraries and upload 44 | - name: Upload macOS native libraries for reuse in publishing job 45 | uses: actions/upload-artifact@v3 46 | with: 47 | # name: no name is required because we upload the entire directory 48 | # the default name "artifact" will be used 49 | path: /Users/runner/work/bdk-kotlin/bdk-kotlin/bdk-jvm/lib/src/main/resources/ 50 | 51 | build-jvm-full-library: 52 | name: Create full bdk-jvm library 53 | needs: [build-jvm-macOS-M1-native-lib] 54 | runs-on: ubuntu-22.04 55 | steps: 56 | - name: Checkout publishing branch 57 | uses: actions/checkout@v2 58 | 59 | - name: Update bdk-ffi git submodule 60 | run: | 61 | git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git 62 | git submodule update --init bdk-ffi 63 | 64 | - name: Cache 65 | uses: actions/cache@v3 66 | with: 67 | path: | 68 | ~/.cargo/registry 69 | ~/.cargo/git 70 | ./bdk-ffi/target 71 | key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} 72 | 73 | - name: Set up JDK 74 | uses: actions/setup-java@v2 75 | with: 76 | distribution: temurin 77 | java-version: 11 78 | 79 | - name: Build bdk-jvm library 80 | run: | 81 | cd bdk-jvm 82 | ./gradlew buildJvmLib 83 | 84 | - name: Download macOS native libraries from previous job 85 | uses: actions/download-artifact@v3 86 | id: download 87 | with: 88 | # download the artifact created in the prior job (named "artifact") 89 | name: artifact 90 | path: ./bdk-jvm/lib/src/main/resources/ 91 | 92 | - name: Publish to Maven Central 93 | env: 94 | ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }} 95 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }} 96 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }} 97 | ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }} 98 | ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }} 99 | run: | 100 | cd bdk-jvm 101 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository 102 | -------------------------------------------------------------------------------- /.github/workflows/test-android.yaml: -------------------------------------------------------------------------------- 1 | name: Test Android 2 | on: [push, pull_request] 3 | 4 | env: 5 | ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529 6 | # By default the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT 7 | # ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - name: Install Android NDK 21.4.7075529 14 | run: | 15 | ANDROID_ROOT=/usr/local/lib/android 16 | ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk 17 | SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager 18 | echo "y" | $SDKMANAGER "ndk;21.4.7075529" 19 | 20 | - name: Check out PR branch 21 | uses: actions/checkout@v2 22 | 23 | - name: Update bdk-ffi git submodule 24 | run: | 25 | git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git 26 | git submodule update --init bdk-ffi 27 | 28 | - name: cache 29 | uses: actions/cache@v2 30 | with: 31 | path: | 32 | ~/.cargo/registry 33 | ~/.cargo/git 34 | bdk-ffi/target 35 | key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} 36 | 37 | - name: Set up JDK 38 | uses: actions/setup-java@v2 39 | with: 40 | distribution: temurin 41 | java-version: 11 42 | 43 | - name: Install rust android targets 44 | run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi 45 | 46 | - name: Build bdk-android library 47 | run: | 48 | cd bdk-android 49 | ./gradlew buildAndroidLib 50 | 51 | - name: Run Android tests 52 | run: | 53 | cd bdk-android 54 | ./gradlew test --console=rich 55 | -------------------------------------------------------------------------------- /.github/workflows/test-jvm.yaml: -------------------------------------------------------------------------------- 1 | name: Test JVM 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-20.04 7 | steps: 8 | - name: Check out PR branch 9 | uses: actions/checkout@v2 10 | 11 | - name: Update bdk-ffi git submodule 12 | run: | 13 | git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git 14 | git submodule update --init bdk-ffi 15 | 16 | - name: cache 17 | uses: actions/cache@v2 18 | with: 19 | path: | 20 | ~/.cargo/registry 21 | ~/.cargo/git 22 | bdk-ffi/target 23 | key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} 24 | 25 | - name: Set up JDK 26 | uses: actions/setup-java@v2 27 | with: 28 | distribution: temurin 29 | java-version: 11 30 | 31 | - name: Build bdk-jvm library 32 | run: | 33 | cd bdk-jvm 34 | ./gradlew buildJvmLib 35 | 36 | - name: Run JVM tests 37 | run: | 38 | cd bdk-jvm 39 | ./gradlew test --console=rich 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | build 3 | Cargo.lock 4 | /bindings/bdk-kotlin/local.properties 5 | .gradle 6 | wallet_db 7 | bdk_ffi_test 8 | local.properties 9 | *.log 10 | *.dylib 11 | *.so 12 | .DS_Store 13 | testdb 14 | xcuserdata 15 | .lsp 16 | .clj-kondo 17 | .idea 18 | bdk-android/lib/src/main/kotlin/org/bitcoindevkit/bdk.kt 19 | bdk-jvm/lib/src/main/kotlin/org/bitcoindevkit/bdk.kt 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bdk-ffi"] 2 | path = bdk-ffi 3 | url = https://github.com/bitcoindevkit/bdk-ffi.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under [Apache 2.0](LICENSE-APACHE) or 2 | [MIT](LICENSE-MIT), at your option. 3 | 4 | Some files retain their own copyright notice, however, for full authorship 5 | information, see version control history. 6 | 7 | Except as otherwise noted in individual files, all files in this repository are 8 | licensed under the Apache License, Version 2.0 or the MIT license , at your option. 11 | 12 | You may not use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | sell copies of this software or any files in this repository except in 14 | accordance with one or both of these licenses. -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 5 | the Software, and to permit persons to whom the Software is furnished to do so, 6 | subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /PGP-BDK-BINDINGS.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mDMEYw6xkRYJKwYBBAHaRw8BAQdAg+VLXuidDqeP015H/QMlESJyQeIntTUoQkbk 4 | +IFu+jO0M2JpdGNvaW5kZXZraXQtYmluZGluZ3MgPGJpbmRpbmdzQGJpdGNvaW5k 5 | ZXZraXQub3JnPoiTBBMWCgA7FiEEiK2TrEWJ/QkP87jRJ2jEPogDxqMFAmMOsZEC 6 | GwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQJ2jEPogDxqPQTgEA292D 7 | RQaxDTJ4k91D0w50Vrd0NSNUwlsERz9XJ64abWABAP99vGMmq2pfrngTQqjLgLe8 8 | 0YhQ+VML2x/B0LSN6MgNuDgEYw6xkRIKKwYBBAGXVQEFAQEHQEkUJv+/Wzx7nNiX 9 | eti3HkeT6ZNAuCExPE4F7jxHNQ1TAwEIB4h4BBgWCgAgFiEEiK2TrEWJ/QkP87jR 10 | J2jEPogDxqMFAmMOsZECGwwACgkQJ2jEPogDxqObPQEA/B0xNew03KM0JP630efG 11 | QT/3Caq/jx86pLwnB7XqWI8BAOKmqrOEiwCBjhaIpzC3/1M+aZuPRUL3V91uPxpM 12 | jFAJ 13 | =vvmK 14 | -----END PGP PUBLIC KEY BLOCK----- -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bdk-kotlin 2 | 3 | ⚠️⚠️ The code in this repository has been moved to [bdk-ffi](https://github.com/bitcoindevkit/bdk-ffi), and development is continuing there. This repository is now considered an archive, and will not accept new pull requests or issues. ⚠️⚠️ 4 | 5 |
6 |
7 |
8 | 9 | This project builds .jar and .aar packages for the `jvm` and `android` platforms that provide 10 | [Kotlin] language bindings for the [`bdk`] library. The Kotlin language bindings are created by the 11 | [`bdk-ffi`] project which is included as a git submodule of this repository. 12 | 13 | ## How to Use 14 | 15 | To use the Kotlin language bindings for [`bdk`] in your `jvm` or `android` project add the 16 | following to your gradle dependencies: 17 | ```groovy 18 | repositories { 19 | mavenCentral() 20 | } 21 | 22 | dependencies { 23 | 24 | // for jvm 25 | implementation 'org.bitcoindevkit:bdk-jvm:' 26 | // OR for android 27 | implementation 'org.bitcoindevkit:bdk-android:' 28 | 29 | } 30 | ``` 31 | 32 | You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example: 33 | 34 | ```kotlin 35 | import org.bitcoindevkit.* 36 | 37 | // ... 38 | 39 | val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" 40 | val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" 41 | 42 | val databaseConfig = DatabaseConfig.Memory 43 | 44 | val blockchainConfig = 45 | BlockchainConfig.Electrum( 46 | ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u) 47 | ) 48 | val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig) 49 | val newAddress = wallet.getNewAddress() 50 | ``` 51 | 52 | ### Example Projects 53 | 54 | #### `bdk-android` 55 | * [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet) 56 | * [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet) 57 | 58 | #### `bdk-jvm` 59 | * [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine) 60 | 61 | ### How to build 62 | _Note that Kotlin version `1.6.10` or later is required to build the library._ 63 | 64 | 1. Clone this repository and initialize and update its [`bdk-ffi`] submodule. 65 | ```shell 66 | git clone https://github.com/bitcoindevkit/bdk-kotlin 67 | git submodule update --init 68 | ``` 69 | 2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions. 70 | 3. If building on MacOS install required intel and m1 jvm targets 71 | ```sh 72 | rustup target add x86_64-apple-darwin aarch64-apple-darwin 73 | ``` 74 | 4. Install required targets 75 | ```sh 76 | rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi 77 | ``` 78 | 5. Install Android SDK and Build-Tools for API level 30+ 79 | 6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the 80 | build tool), for example (NDK major version 21 is required): 81 | ```shell 82 | export ANDROID_SDK_ROOT=~/Android/Sdk 83 | export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21. 84 | ``` 85 | 7. Build kotlin bindings 86 | ```sh 87 | # build JVM library 88 | cd bdk-jvm 89 | ./gradlew buildJvmLib 90 | 91 | # build Android library 92 | cd bdk-android 93 | ./gradlew buildAndroidLib 94 | ``` 95 | 8. Start android emulator (must be x86_64) and run tests 96 | ```sh 97 | ./gradlew connectedAndroidTest 98 | ``` 99 | 100 | ## How to publish 101 | 102 | ### Publish to your local maven repo 103 | ```shell 104 | # bdk-jvm 105 | cd bdk-jvm 106 | ./gradlew publishToMavenLocal --exclude-task signMavenPublication 107 | 108 | # bdk-android 109 | cd bdk-android 110 | ./gradlew publishToMavenLocal --exclude-task signMavenPublication 111 | ``` 112 | 113 | Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so: 114 | ```properties 115 | signing.gnupg.keyName= 116 | signing.gnupg.passphrase= 117 | ``` 118 | 119 | and use the `publishToMavenLocal` task without excluding the signing task: 120 | ```shell 121 | ./gradlew publishToMavenLocal 122 | ``` 123 | 124 | ## Verifying Signatures 125 | Both libraries and all their corresponding artifacts are signed with a PGP key you can find in the 126 | root of this repository. To verify the signatures follow the below steps: 127 | 128 | 1. Import the PGP key in your keyring. 129 | ```shell 130 | # Navigate to the root of the repository and import the ./PGP-BDK-BINDINGS.asc public key 131 | gpg --import ./PGP-BDK-BINDINGS.asc 132 | 133 | # Alternatively, you can import the key directly from a public key server 134 | gpg --keyserver keyserver.ubuntu.com --receive-key 2768C43E8803C6A3 135 | 136 | # Verify that the correct key was imported 137 | gpg --list-keys 138 | # You should see the below output 139 | pub ed25519 2022-08-31 [SC] 140 | 88AD93AC4589FD090FF3B8D12768C43E8803C6A3 141 | uid [ unknown] bitcoindevkit-bindings 142 | sub cv25519 2022-08-31 [E] 143 | ``` 144 | 145 | 2. Download the binary artifacts and corresponding signature files. 146 | - from [bdk-jvm] 147 | - `bdk-jvm-.jar` 148 | - `bdk-jvm-.jar.asc` 149 | - from [bdk-android] 150 | - `bdk-android-.aar` 151 | - `bdk-android-.aar.asc` 152 | 153 | 3. Verify the signatures. 154 | ```shell 155 | gpg --verify bdk-jvm-.jar.asc 156 | gpg --verify bdk-android-.aar.asc 157 | 158 | # you should see a "Good signature" result 159 | gpg: Good signature from "bitcoindevkit-bindings " [unknown] 160 | ``` 161 | 162 | ### PGP Metadata 163 | Full key ID: `88AD 93AC 4589 FD09 0FF3 B8D1 2768 C43E 8803 C6A3` 164 | Fingerprint: `2768C43E8803C6A3` 165 | Name: `bitcoindevkit-bindings` 166 | Email: `bindings@bitcoindevkit.org` 167 | 168 | [Kotlin]: https://kotlinlang.org/ 169 | [Android Studio]: https://developer.android.com/studio/ 170 | [`bdk`]: https://github.com/bitcoindevkit/bdk 171 | [`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi 172 | ["Getting Started (Developer)"]: https://github.com/bitcoindevkit/bdk-ffi#getting-started-developer 173 | [Gradle Nexus Publish Plugin]: https://github.com/gradle-nexus/publish-plugin 174 | [bdk-jvm]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-jvm/0.9.0/jar 175 | [bdk-android]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-android/0.9.0/aar 176 | -------------------------------------------------------------------------------- /api-docs/Module1.md: -------------------------------------------------------------------------------- 1 | # Module bdk-android 2 | The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android. 3 | 4 | # Package org.bitcoindevkit 5 | -------------------------------------------------------------------------------- /api-docs/Module2.md: -------------------------------------------------------------------------------- 1 | # Module bdk-jvm 2 | The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM. 3 | 4 | # Package org.bitcoindevkit 5 | -------------------------------------------------------------------------------- /api-docs/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | kotlin("jvm") version "1.7.10" 5 | 6 | // API docs 7 | id("org.jetbrains.dokka") version "1.7.10" 8 | } 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation(kotlin("test")) 16 | } 17 | 18 | tasks.test { 19 | useJUnitPlatform() 20 | } 21 | 22 | tasks.withType { 23 | kotlinOptions.jvmTarget = "1.8" 24 | } 25 | 26 | tasks.withType().configureEach { 27 | dokkaSourceSets { 28 | named("main") { 29 | moduleName.set("bdk-android") 30 | moduleVersion.set("0.11.0") 31 | includes.from("Module1.md") 32 | samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt") 33 | } 34 | } 35 | } 36 | 37 | // tasks.withType().configureEach { 38 | // dokkaSourceSets { 39 | // named("main") { 40 | // moduleName.set("bdk-jvm") 41 | // moduleVersion.set("0.11.0") 42 | // includes.from("Module2.md") 43 | // samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt") 44 | // } 45 | // } 46 | // } 47 | -------------------------------------------------------------------------------- /api-docs/deploy.sh: -------------------------------------------------------------------------------- 1 | ./gradlew dokkaHtml 2 | cd build/dokka/html 3 | git init . 4 | git add . 5 | git switch --create gh-pages 6 | git commit -m "Deploy" 7 | git remote add origin git@github.com:bitcoindevkit/bdk-kotlin.git 8 | git push --set-upstream origin gh-pages --force 9 | -------------------------------------------------------------------------------- /api-docs/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | -------------------------------------------------------------------------------- /api-docs/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoindevkit/bdk-kotlin/12a7233c1cf789e257c9e80fb59caa0a53c14018/api-docs/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /api-docs/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /api-docs/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /api-docs/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /api-docs/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "BDK Android and BDK JVM API Docs" 2 | -------------------------------------------------------------------------------- /api-docs/src/main/kotlin/org/bitcoindevkit/bdk.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit 2 | 3 | /** 4 | * The cryptocurrency to act on. 5 | * 6 | * @sample org.bitcoindevkit.networkSample 7 | */ 8 | enum class Network { 9 | /** Bitcoin's mainnet. */ 10 | BITCOIN, 11 | 12 | /** Bitcoin’s testnet. */ 13 | TESTNET, 14 | 15 | /** Bitcoin’s signet. */ 16 | SIGNET, 17 | 18 | /** Bitcoin’s regtest. */ 19 | REGTEST, 20 | } 21 | 22 | /** 23 | * A derived address and the index it was found at. 24 | * 25 | * @property index Child index of this address. 26 | * @property address Address. 27 | * 28 | * @sample org.bitcoindevkit.addressInfoSample 29 | */ 30 | data class AddressInfo ( 31 | var index: UInt, 32 | var address: String 33 | ) 34 | 35 | /** 36 | * The address index selection strategy to use to derive an address from the wallet’s external descriptor. 37 | * 38 | * If you’re unsure which one to use, use `AddressIndex.NEW`. 39 | * 40 | * @sample org.bitcoindevkit.addressIndexSample 41 | */ 42 | enum class AddressIndex { 43 | /** Return a new address after incrementing the current descriptor index. */ 44 | NEW, 45 | 46 | /** Return the address for the current descriptor index if it has not been used in a received transaction. 47 | * Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet 48 | * has not yet detected an address has been used it could return an already used address. 49 | * This function is primarily meant for situations where the caller is untrusted; 50 | * for example when deriving donation addresses on-demand for a public web page. 51 | */ 52 | LAST_UNUSED, 53 | } 54 | 55 | /** 56 | * Balance differentiated in various categories. 57 | * 58 | * @property immature All coinbase outputs not yet matured. 59 | * @property trustedPending Unconfirmed UTXOs generated by a wallet tx. 60 | * @property untrustedPending Unconfirmed UTXOs received from an external wallet. 61 | * @property confirmed Confirmed and immediately spendable balance. 62 | * @property spendable The sum of trustedPending and confirmed coins. 63 | * @property total The whole balance visible to the wallet. 64 | * 65 | * @sample org.bitcoindevkit.balanceSample 66 | */ 67 | data class Balance ( 68 | var immature: ULong, 69 | var trustedPending: ULong, 70 | var untrustedPending: ULong, 71 | var confirmed: ULong, 72 | var spendable: ULong, 73 | var total: ULong 74 | ) 75 | 76 | /** 77 | * Type that can contain any of the database configurations defined by the library. 78 | * 79 | * @sample org.bitcoindevkit.memoryDatabaseConfigSample 80 | * @sample org.bitcoindevkit.sqliteDatabaseConfigSample 81 | */ 82 | sealed class DatabaseConfig { 83 | /** Configuration for an in-memory database. */ 84 | object Memory : DatabaseConfig() 85 | 86 | /** Configuration for a Sled database. */ 87 | data class Sled(val config: SledDbConfiguration) : DatabaseConfig() 88 | 89 | /** Configuration for a SQLite database. */ 90 | data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig() 91 | } 92 | 93 | /** 94 | * Configuration type for a SQLite database. 95 | * 96 | * @property path Main directory of the DB. 97 | */ 98 | data class SqliteDbConfiguration( 99 | var path: String, 100 | ) 101 | 102 | /** 103 | * Configuration type for a SledDB database. 104 | * 105 | * @property path Main directory of the DB. 106 | * @property treeName Name of the database tree, a separated namespace for the data. 107 | */ 108 | data class SledDbConfiguration( 109 | var path: String, 110 | var treeName: String, 111 | ) 112 | 113 | /** 114 | * Configuration for an Electrum blockchain. 115 | * 116 | * @property url 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`. 117 | * @property socks5 URL of the socks5 proxy server or a Tor service. 118 | * @property retry Request retry count. 119 | * @property timeout Request timeout (seconds). 120 | * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length. 121 | * 122 | * @sample org.bitcoindevkit.electrumBlockchainConfigSample 123 | */ 124 | data class ElectrumConfig ( 125 | var url: String, 126 | var socks5: String?, 127 | var retry: UByte, 128 | var timeout: UByte?, 129 | var stopGap: ULong 130 | ) 131 | 132 | /** 133 | * Configuration for an Esplora blockchain. 134 | * 135 | * @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`. 136 | * @property proxy Optional URL of the proxy to use to make requests to the Esplora server. 137 | * @property concurrency Number of parallel requests sent to the esplora service (default: 4). 138 | * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length. 139 | * @property timeout Socket timeout. 140 | */ 141 | data class EsploraConfig ( 142 | var baseUrl: String, 143 | var proxy: String?, 144 | var concurrency: UByte?, 145 | var stopGap: ULong, 146 | var timeout: ULong? 147 | ) 148 | 149 | /** 150 | * Type that can contain any of the blockchain configurations defined by the library. 151 | * 152 | * @sample org.bitcoindevkit.electrumBlockchainConfigSample 153 | */ 154 | sealed class BlockchainConfig { 155 | /** Electrum client. */ 156 | data class Electrum(val config: ElectrumConfig) : BlockchainConfig() 157 | 158 | /** Esplora client. */ 159 | data class Esplora(val config: EsploraConfig) : BlockchainConfig() 160 | } 161 | 162 | /** 163 | * A wallet transaction. 164 | * 165 | * @property fee 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. 166 | * @property received Received value (sats) Sum of owned outputs of this transaction. 167 | * @property sent Sent value (sats) Sum of owned inputs of this transaction. 168 | * @property txid Transaction id. 169 | * @property confirmationTime If the transaction is confirmed, [BlockTime] contains height and timestamp of the block containing the transaction. This property is null for unconfirmed transactions. 170 | */ 171 | data class TransactionDetails ( 172 | var fee: ULong?, 173 | var received: ULong, 174 | var sent: ULong, 175 | var txid: String, 176 | var confirmationTime: BlockTime? 177 | ) 178 | 179 | /** 180 | * A blockchain backend. 181 | * 182 | * @constructor Create the new blockchain client. 183 | * 184 | * @param config The blockchain configuration required. 185 | * 186 | * @sample org.bitcoindevkit.blockchainSample 187 | */ 188 | class Blockchain( 189 | config: BlockchainConfig 190 | ) { 191 | /** Broadcast a transaction. */ 192 | fun broadcast(psbt: PartiallySignedBitcoinTransaction): String {} 193 | 194 | /** Get the current height of the blockchain. */ 195 | fun getHeight(): UInt {} 196 | 197 | /** Get the block hash of a given block. */ 198 | fun getBlockHash(height: UInt): String {} 199 | } 200 | 201 | /** 202 | * A partially signed bitcoin transaction. 203 | * 204 | * @constructor Build a new Partially Signed Bitcoin Transaction. 205 | * 206 | * @param psbtBase64 The PSBT in base64 format. 207 | */ 208 | class PartiallySignedBitcoinTransaction(psbtBase64: String) { 209 | /** Return the PSBT in string format, using a base64 encoding. */ 210 | fun serialize(): String {} 211 | 212 | /** Get the txid of the PSBT. */ 213 | fun txid(): String {} 214 | 215 | /** Return the transaction as bytes. */ 216 | fun `extractTx`(): List 217 | 218 | /** 219 | * Combines this PartiallySignedTransaction with another PSBT as described by BIP 174. 220 | * In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)` 221 | */ 222 | fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction 223 | } 224 | 225 | /** 226 | * A reference to a transaction output. 227 | * 228 | * @property txid The referenced transaction’s txid. 229 | * @property vout The index of the referenced output in its transaction’s vout. 230 | */ 231 | data class OutPoint ( 232 | var txid: String, 233 | var vout: UInt 234 | ) 235 | 236 | /** 237 | * A transaction output, which defines new coins to be created from old ones. 238 | * 239 | * @property value The value of the output, in satoshis. 240 | * @property address The address of the output. 241 | */ 242 | data class TxOut ( 243 | var value: ULong, 244 | var address: String 245 | ) 246 | 247 | /** 248 | * An unspent output owned by a [Wallet]. 249 | * 250 | * @property outpoint Reference to a transaction output. 251 | * @property txout Transaction output. 252 | * @property keychain Type of keychain. 253 | * @property isSpent Whether this UTXO is spent or not. 254 | */ 255 | data class LocalUtxo ( 256 | var outpoint: OutPoint, 257 | var txout: TxOut, 258 | var keychain: KeychainKind, 259 | var isSpent: Boolean 260 | ) 261 | 262 | /** 263 | * Types of keychains. 264 | */ 265 | enum class KeychainKind { 266 | /** External. */ 267 | EXTERNAL, 268 | 269 | /** Internal, usually used for change outputs. */ 270 | INTERNAL, 271 | } 272 | 273 | /** 274 | * Block height and timestamp of a block. 275 | * 276 | * @property height Confirmation block height. 277 | * @property timestamp Confirmation block timestamp. 278 | */ 279 | data class BlockTime ( 280 | var height: UInt, 281 | var timestamp: ULong, 282 | ) 283 | 284 | /** 285 | * A Bitcoin wallet. 286 | * The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are: 287 | * 1. Output descriptors from which it can derive addresses. 288 | * 2. A Database where it tracks transactions and utxos related to the descriptors. 289 | * 3. Signers that can contribute signatures to addresses instantiated from the descriptors. 290 | * 291 | * @constructor Create a BDK wallet. 292 | * 293 | * @param descriptor The main (or "external") descriptor. 294 | * @param changeDescriptor The change (or "internal") descriptor. 295 | * @param network The network to act on. 296 | * @param databaseConfig The database configuration. 297 | * 298 | * @sample org.bitcoindevkit.walletSample 299 | */ 300 | class Wallet( 301 | descriptor: String, 302 | changeDescriptor: String, 303 | network: Network, 304 | databaseConfig: DatabaseConfig, 305 | ) { 306 | /** 307 | * Return a derived address using the external descriptor, see [AddressIndex] for available address index 308 | * selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end 309 | * with a * character) then the same address will always be returned for any [AddressIndex]. 310 | */ 311 | fun getAddress(addressIndex: AddressIndex): AddressInfo {} 312 | 313 | /** 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. */ 314 | fun getBalance(): ULong {} 315 | 316 | /** Sign a transaction with all the wallet’s signers. */ 317 | fun sign(psbt: PartiallySignedBitcoinTransaction): Boolean {} 318 | 319 | /** 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. */ 320 | fun listTransactions(): List {} 321 | 322 | /** Get the Bitcoin network the wallet is using. */ 323 | fun network(): Network {} 324 | 325 | /** Sync the internal database with the blockchain. */ 326 | fun sync(blockchain: Blockchain, progress: Progress?) {} 327 | 328 | /** 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. */ 329 | fun listUnspent(): List {} 330 | } 331 | 332 | /** 333 | * Class that logs at level INFO every update received (if any). 334 | */ 335 | class Progress { 336 | /** 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. */ 337 | fun update(progress: Float, message: String?) {} 338 | } 339 | 340 | /** 341 | * A transaction builder. 342 | * 343 | * After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction. 344 | * 345 | * Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added. 346 | */ 347 | class TxBuilder() { 348 | /** Add data as an output using OP_RETURN. */ 349 | fun addData(data: List): TxBuilder {} 350 | 351 | /** Add a recipient to the internal list. */ 352 | fun addRecipient(script: Script, amount: ULong): TxBuilder {} 353 | 354 | /** Set the list of recipients by providing a list of [AddressAmount]. */ 355 | fun setRecipients(recipients: List): TxBuilder {} 356 | 357 | /** 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. */ 358 | fun addUnspendable(unspendable: OutPoint): TxBuilder {} 359 | 360 | /** 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. */ 361 | fun addUtxo(outpoint: OutPoint): TxBuilder {} 362 | 363 | /** 364 | * Add the list of outpoints to the internal list of UTXOs that must be spent. If an error 365 | * occurs while adding any of the UTXOs then none of them are added and the error is returned. 366 | * These have priority over the "unspendable" utxos, meaning that if a utxo is present both 367 | * in the "utxos" and the "unspendable" list, it will be spent. 368 | */ 369 | fun addUtxos(outpoints: List): TxBuilder {} 370 | 371 | /** Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */ 372 | fun doNotSpendChange(): TxBuilder {} 373 | 374 | /** 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. */ 375 | fun manuallySelectedOnly(): TxBuilder {} 376 | 377 | /** Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */ 378 | fun onlySpendChange(): TxBuilder {} 379 | 380 | /** 381 | * Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos 382 | * added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details. 383 | */ 384 | fun unspendable(unspendable: List): TxBuilder {} 385 | 386 | /** Set a custom fee rate. */ 387 | fun feeRate(satPerVbyte: Float): TxBuilder {} 388 | 389 | /** Set an absolute fee. */ 390 | fun feeAbsolute(feeAmount: ULong): TxBuilder {} 391 | 392 | /** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */ 393 | fun drainWallet(): TxBuilder {} 394 | 395 | /** 396 | * Sets the address to drain excess coins to. Usually, when there are excess coins they are 397 | * sent to a change address generated by the wallet. This option replaces the usual change address 398 | * with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the 399 | * drain output is not needed (the excess coins are too small) it will not be included in the resulting 400 | * transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients 401 | * with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any 402 | * recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set 403 | * [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option, 404 | * you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees. 405 | */ 406 | fun drainTo(address: String): TxBuilder {} 407 | 408 | /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */ 409 | fun enableRbf(): TxBuilder {} 410 | 411 | /** 412 | * Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors 413 | * contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` 414 | * is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF. 415 | */ 416 | fun enableRbfWithSequence(nsequence: UInt): TxBuilder {} 417 | 418 | /** Finish building the transaction. Returns a [TxBuilderResult]. */ 419 | fun finish(wallet: Wallet): TxBuilderResult {} 420 | } 421 | 422 | /** 423 | * A object holding an ScriptPubKey and an amount. 424 | * 425 | * @property script The ScriptPubKey. 426 | * @property amount The amount. 427 | */ 428 | data class AddressAmount ( 429 | var script: Script, 430 | var amount: ULong 431 | ) 432 | 433 | /** 434 | * The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true. 435 | */ 436 | class BumpFeeTxBuilder() { 437 | /** 438 | * Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this scriptPubKey 439 | * in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output 440 | * to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is 441 | * preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error 442 | * if scriptPubkey can’t be found among the recipients of the transaction we are bumping. 443 | */ 444 | fun allowShrinking(address: String): BumpFeeTxBuilder {} 445 | 446 | /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */ 447 | fun enableRbf(): BumpFeeTxBuilder {} 448 | 449 | /** 450 | * Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors 451 | * contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` 452 | * is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF. 453 | */ 454 | fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {} 455 | 456 | /** Finish building the transaction. Returns a [TxBuilderResult]. */ 457 | fun finish(wallet: Wallet): TxBuilderResult {} 458 | } 459 | 460 | /** 461 | * A BIP-32 derivation path. 462 | * 463 | * @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey] 464 | * or [DescriptorPublicKey]. 465 | */ 466 | class DerivationPath(path: String) {} 467 | 468 | /** 469 | * An extended secret key. 470 | * 471 | * @param network The network this DescriptorSecretKey is to be used on. 472 | * @param mnemonic The mnemonic. 473 | * @param password The optional passphrase that can be provided as per BIP-39. 474 | * 475 | * @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample 476 | * @sample org.bitcoindevkit.descriptorSecretKeyExtendSample 477 | */ 478 | class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String?) { 479 | /** Derive a private descriptor at a given path. */ 480 | fun derive(path: DerivationPath): DescriptorSecretKey {} 481 | 482 | /** Extend the private descriptor with a custom path. */ 483 | fun extend(path: DerivationPath): DescriptorSecretKey {} 484 | 485 | /** Return the public version of the descriptor. */ 486 | fun asPublic(): DescriptorPublicKey {} 487 | 488 | /* Return the raw private key as bytes. */ 489 | fun secretBytes(): List 490 | 491 | /** Return the private descriptor as a string. */ 492 | fun asString(): String {} 493 | } 494 | 495 | /** 496 | * An extended public key. 497 | * 498 | * @param network The network this DescriptorPublicKey is to be used on. 499 | * @param mnemonic The mnemonic. 500 | * @param password The optional passphrase that can be provided as per BIP-39. 501 | */ 502 | class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) { 503 | /** Derive a public descriptor at a given path. */ 504 | fun derive(path: DerivationPath): DescriptorSecretKey 505 | 506 | /** Extend the public descriptor with a custom path. */ 507 | fun extend(path: DerivationPath): DescriptorSecretKey 508 | 509 | /** Return the public descriptor as a string. */ 510 | fun asString(): String 511 | } 512 | 513 | /** 514 | * An enum describing entropy length (aka word count) in the mnemonic. 515 | */ 516 | enum class WordCount { 517 | /** 12 words mnemonic (128 bits entropy). */ 518 | WORDS12, 519 | 520 | /** 15 words mnemonic (160 bits entropy). */ 521 | WORDS15, 522 | 523 | /** 18 words mnemonic (192 bits entropy). */ 524 | WORDS18, 525 | 526 | /** 21 words mnemonic (224 bits entropy). */ 527 | WORDS21, 528 | 529 | /** 24 words mnemonic (256 bits entropy). */ 530 | WORDS24, 531 | } 532 | 533 | /** 534 | * The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder]. 535 | * 536 | * @property psbt The PSBT 537 | * @property transactionDetails The transaction details. 538 | * 539 | * @sample org.bitcoindevkit.txBuilderResultSample1 540 | * @sample org.bitcoindevkit.txBuilderResultSample2 541 | */ 542 | data class TxBuilderResult ( 543 | var psbt: PartiallySignedBitcoinTransaction, 544 | var transactionDetails: TransactionDetails 545 | ) 546 | 547 | /** 548 | * A bitcoin script. 549 | */ 550 | class Script(rawOutputScript: List) 551 | 552 | /** 553 | * A bitcoin address. 554 | * 555 | * @param address The address in string format. 556 | */ 557 | class Address(address: String) { 558 | /* Return the ScriptPubKey. */ 559 | fun scriptPubkey(): Script 560 | } 561 | 562 | /** 563 | * Mnemonic phrases are a human-readable version of the private keys. Supported number of words are 12, 15, 18, 21 and 24. 564 | * 565 | * @constructor Generates Mnemonic with a random entropy. 566 | * @param mnemonic The mnemonic as a string of space-separated words. 567 | * 568 | * @sample org.bitcoindevkit.mnemonicSample 569 | */ 570 | class Mnemonic(mnemonic: String) { 571 | /* Returns Mnemonic as string */ 572 | fun asString(): String 573 | 574 | /* Parse a Mnemonic from a given string. */ 575 | fun fromString(): Mnemonic 576 | 577 | /* 578 | * Create a new Mnemonic in the specified language from the given entropy. Entropy must be a 579 | * multiple of 32 bits (4 bytes) and 128-256 bits in length. 580 | */ 581 | fun fromEntropy(): Mnemonic 582 | } 583 | -------------------------------------------------------------------------------- /api-docs/src/test/kotlin/org/bitcoindevkit/Samples.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit 2 | 3 | fun networkSample() { 4 | val wallet = Wallet( 5 | descriptor = descriptor, 6 | changeDescriptor = changeDescriptor, 7 | network = Network.TESTNET, 8 | databaseConfig = DatabaseConfig.Memory 9 | ) 10 | } 11 | 12 | fun balanceSample() { 13 | object LogProgress : Progress { 14 | override fun update(progress: Float, message: String?) {} 15 | } 16 | 17 | val memoryDatabaseConfig = DatabaseConfig.Memory 18 | private val blockchainConfig = BlockchainConfig.Electrum( 19 | ElectrumConfig( 20 | "ssl://electrum.blockstream.info:60002", 21 | null, 22 | 5u, 23 | null, 24 | 200u 25 | ) 26 | ) 27 | val wallet = Wallet(descriptor, null, Network.TESTNET, memoryDatabaseConfig) 28 | val blockchain = Blockchain(blockchainConfig) 29 | wallet.sync(blockchain, LogProgress) 30 | 31 | val balance: Balance = wallet.getBalance() 32 | println("Total wallet balance is ${balance.total}") 33 | } 34 | 35 | fun electrumBlockchainConfigSample() { 36 | val blockchainConfig = BlockchainConfig.Electrum( 37 | ElectrumConfig( 38 | url = "ssl://electrum.blockstream.info:60002", 39 | socks5 = null, 40 | retry = 5u, 41 | timeout = null, 42 | stopGap = 200u 43 | ) 44 | ) 45 | } 46 | 47 | fun memoryDatabaseConfigSample() { 48 | val memoryDatabaseConfig = DatabaseConfig.Memory 49 | } 50 | 51 | fun sqliteDatabaseConfigSample() { 52 | val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite")) 53 | } 54 | 55 | fun addressIndexSample() { 56 | val wallet: Wallet = Wallet( 57 | descriptor = descriptor, 58 | changeDescriptor = changeDescriptor, 59 | network = Network.TESTNET, 60 | databaseConfig = DatabaseConfig.Memory 61 | ) 62 | 63 | fun getLastUnusedAddress(): AddressInfo { 64 | return wallet.getAddress(AddressIndex.LAST_UNUSED) 65 | } 66 | } 67 | 68 | fun addressInfoSample() { 69 | val wallet: Wallet = Wallet( 70 | descriptor = descriptor, 71 | changeDescriptor = changeDescriptor, 72 | network = Network.TESTNET, 73 | databaseConfig = DatabaseConfig.Memory 74 | ) 75 | 76 | fun getLastUnusedAddress(): AddressInfo { 77 | return wallet.getAddress(AddressIndex.NEW) 78 | } 79 | 80 | val newAddress: AddressInfo = getLastUnusedAddress() 81 | 82 | println("New address at index ${newAddress.index} is ${newAddress.address}") 83 | } 84 | 85 | fun blockchainSample() { 86 | val blockchainConfig: BlockchainConfig = BlockchainConfig.Electrum( 87 | ElectrumConfig( 88 | electrumURL, 89 | null, 90 | 5u, 91 | null, 92 | 10u 93 | ) 94 | ) 95 | 96 | val blockchain: Blockchain = Blockchain(blockchainConfig) 97 | 98 | blockchain.broadcast(signedPsbt) 99 | } 100 | 101 | 102 | fun txBuilderResultSample1() { 103 | val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") 104 | // TxBuilderResult is a data class, which means you can use destructuring declarations on it to 105 | // open it up in its component parts 106 | val (psbt, txDetails) = TxBuilder() 107 | .addRecipient(faucetAddress.scriptPubkey(), 1000u) 108 | .feeRate(1.2f) 109 | .finish(wallet) 110 | 111 | println("Txid is ${txDetails.txid}") 112 | wallet.sign(psbt) 113 | } 114 | 115 | fun txBuilderResultSample2() { 116 | val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") 117 | val txBuilderResult: TxBuilderResult = TxBuilder() 118 | .addRecipient(faucetAddress.scriptPubkey(), 1000u) 119 | .feeRate(1.2f) 120 | .finish(wallet) 121 | 122 | val psbt = txBuilderResult.psbt 123 | val txDetails = txBuilderResult.transactionDetails 124 | 125 | println("Txid is ${txDetails.txid}") 126 | wallet.sign(psbt) 127 | } 128 | 129 | fun descriptorSecretKeyExtendSample() { 130 | // The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path. 131 | 132 | // val mnemonic: String = generateMnemonic(WordCount.WORDS12) 133 | val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual") 134 | 135 | // the initial DescriptorSecretKey will always be at the "master" node, 136 | // i.e. the derivation path is empty 137 | val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey( 138 | network = Network.TESTNET, 139 | mnemonic = mnemonic, 140 | password = "" 141 | ) 142 | println(bip32RootKey.asString()) 143 | // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/* 144 | 145 | // the derive method will also automatically apply the wildcard (*) to your path, 146 | // i.e the following will generate the typical testnet BIP84 external wallet path 147 | // m/84h/1h/0h/0/* 148 | val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0") 149 | val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString() 150 | println(externalExtendedKey) 151 | // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/* 152 | 153 | // to create the descriptor you'll need to use this extended key in a descriptor function, 154 | // i.e. wpkh(), tr(), etc. 155 | val externalDescriptor = "wpkh($externalExtendedKey)" 156 | } 157 | 158 | fun descriptorSecretKeyDeriveSample() { 159 | // The DescriptorSecretKey.derive() method allows you to derive an extended key for a given 160 | // node in the derivation tree (for example to create an xpub for a particular account) 161 | 162 | val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual") 163 | val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey( 164 | network = Network.TESTNET, 165 | mnemonic = mnemonic, 166 | password = "" 167 | ) 168 | 169 | val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h") 170 | val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0) 171 | println(xpubAccount0.asString()) 172 | // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/* 173 | 174 | val internalPath: DerivationPath = DerivationPath("m/0") 175 | val externalExtendedKey = xpubAccount0.extend(internalPath).asString() 176 | println(externalExtendedKey) 177 | // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/* 178 | 179 | // to create the descriptor you'll need to use this extended key in a descriptor function, 180 | // i.e. wpkh(), tr(), etc. 181 | val externalDescriptor = "wpkh($externalExtendedKey)" 182 | } 183 | 184 | fun createTransaction() { 185 | val wallet = BdkWallet( 186 | descriptor = externalDescriptor, 187 | changeDescriptor = internalDescriptor, 188 | network = Network.TESTNET, 189 | databaseConfig = memoryDatabaseConfig, 190 | ) 191 | val blockchainConfig = BlockchainConfig.Electrum( 192 | ElectrumConfig( 193 | "ssl://electrum.blockstream.info:60002", 194 | null, 195 | 5u, 196 | null, 197 | 200u 198 | ) 199 | ) 200 | 201 | val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt") 202 | val (psbt, txDetails) = TxBuilder() 203 | .addRecipient(faucetAddress.scriptPubkey(), 1000u) 204 | .feeRate(1.2f) 205 | .finish(wallet) 206 | 207 | wallet.sign(psbt) 208 | blockchain.broadcast(psbt) 209 | } 210 | 211 | fun walletSample() { 212 | val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)" 213 | val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)" 214 | val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite")) 215 | 216 | val wallet = BdkWallet( 217 | descriptor = externalDescriptor, 218 | changeDescriptor = internalDescriptor, 219 | network = Network.TESTNET, 220 | databaseConfig = sqliteDatabaseConfig, 221 | ) 222 | } 223 | 224 | fun mnemonicSample() { 225 | val mnemonic0: Mnemonic = Mnemonic(WordCount.WORDS12) 226 | 227 | val mnemonic1: Mnemonic = Mnemonic.fromString("scene change clap smart together mind wheel knee clip normal trial unusual") 228 | 229 | val entropy: List = listOf(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u) 230 | val mnemonic2: Mnemonic = Mnemonic.fromEntropy(entropy) 231 | 232 | println(mnemonic0.asString(), mnemonic1.asString(), mnemonic2.asString()) 233 | } 234 | -------------------------------------------------------------------------------- /bdk-android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | } 5 | dependencies { 6 | classpath("com.android.tools.build:gradle:7.1.2") 7 | } 8 | } 9 | 10 | plugins { 11 | id("io.github.gradle-nexus.publish-plugin") version "1.1.0" 12 | } 13 | 14 | // These properties are required here so that the nexus publish-plugin 15 | // finds a staging profile with the correct group (group is otherwise set as "") 16 | // and knows whether to publish to a SNAPSHOT repository or not 17 | // https://github.com/gradle-nexus/publish-plugin#applying-the-plugin 18 | group = "org.bitcoindevkit" 19 | version = "0.12.0-SNAPSHOT" 20 | 21 | nexusPublishing { 22 | repositories { 23 | create("sonatype") { 24 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) 25 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 26 | 27 | val ossrhUsername: String? by project 28 | val ossrhPassword: String? by project 29 | username.set(ossrhUsername) 30 | password.set(ossrhPassword) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bdk-android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536m 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | kotlin.code.style=official 5 | -------------------------------------------------------------------------------- /bdk-android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoindevkit/bdk-kotlin/12a7233c1cf789e257c9e80fb59caa0a53c14018/bdk-android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /bdk-android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /bdk-android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /bdk-android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /bdk-android/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") version "1.6.10" 4 | id("maven-publish") 5 | id("signing") 6 | 7 | // Custom plugin to generate the native libs and bindings file 8 | id("org.bitcoindevkit.plugins.generate-android-bindings") 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | google() 14 | } 15 | 16 | android { 17 | compileSdk = 31 18 | 19 | defaultConfig { 20 | minSdk = 21 21 | targetSdk = 31 22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 23 | consumerProguardFiles("consumer-rules.pro") 24 | } 25 | 26 | buildTypes { 27 | getByName("release") { 28 | isMinifyEnabled = false 29 | proguardFiles(file("proguard-android-optimize.txt"), file("proguard-rules.pro")) 30 | } 31 | } 32 | 33 | publishing { 34 | singleVariant("release") { 35 | withSourcesJar() 36 | withJavadocJar() 37 | } 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation("net.java.dev.jna:jna:5.8.0@aar") 43 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") 44 | implementation("androidx.appcompat:appcompat:1.4.0") 45 | implementation("androidx.core:core-ktx:1.7.0") 46 | api("org.slf4j:slf4j-api:1.7.30") 47 | 48 | androidTestImplementation("com.github.tony19:logback-android:2.0.0") 49 | androidTestImplementation("androidx.test.ext:junit:1.1.3") 50 | androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") 51 | androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1") 52 | } 53 | 54 | afterEvaluate { 55 | publishing { 56 | publications { 57 | create("maven") { 58 | groupId = "org.bitcoindevkit" 59 | artifactId = "bdk-android" 60 | version = "0.12.0-SNAPSHOT" 61 | 62 | from(components["release"]) 63 | pom { 64 | name.set("bdk-android") 65 | description.set("Bitcoin Dev Kit Kotlin language bindings.") 66 | url.set("https://bitcoindevkit.org") 67 | licenses { 68 | license { 69 | name.set("APACHE 2.0") 70 | url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE") 71 | } 72 | license { 73 | name.set("MIT") 74 | url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT") 75 | } 76 | } 77 | developers { 78 | developer { 79 | id.set("notmandatory") 80 | name.set("Steve Myers") 81 | email.set("notmandatory@noreply.github.org") 82 | } 83 | developer { 84 | id.set("artfuldev") 85 | name.set("Sudarsan Balaji") 86 | email.set("sudarsan.balaji@artfuldev.com") 87 | } 88 | } 89 | scm { 90 | connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git") 91 | developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git") 92 | url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master") 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | signing { 101 | val signingKeyId: String? by project 102 | val signingKey: String? by project 103 | val signingPassword: String? by project 104 | useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) 105 | sign(publishing.publications) 106 | } 107 | -------------------------------------------------------------------------------- /bdk-android/lib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # for JNA 24 | -dontwarn java.awt.* 25 | -keep class com.sun.jna.* { *; } 26 | -keep class org.bitcoindevkit.* { *; } 27 | -keepclassmembers class * extends org.bitcoindevkit.* { public *; } 28 | -keepclassmembers class * extends com.sun.jna.* { public *; } 29 | -------------------------------------------------------------------------------- /bdk-android/lib/src/androidTest/assets/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %logger{12} 5 | 6 | 7 | [%-20thread] %msg 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit 2 | 3 | import org.junit.Assert.* 4 | import org.junit.Test 5 | import android.app.Application 6 | import android.content.Context.MODE_PRIVATE 7 | import androidx.test.core.app.ApplicationProvider 8 | import androidx.test.ext.junit.runners.AndroidJUnit4 9 | import org.junit.runner.RunWith 10 | import org.slf4j.Logger 11 | import org.slf4j.LoggerFactory 12 | import java.io.File 13 | 14 | /** 15 | * Instrumented test, which will execute on an Android device. 16 | * 17 | * See [testing documentation](http://d.android.com/tools/testing). 18 | */ 19 | @RunWith(AndroidJUnit4::class) 20 | class AndroidLibTest { 21 | 22 | private fun getTestDataDir(): String { 23 | val context = ApplicationProvider.getApplicationContext() 24 | return context.getDir("bdk-test", MODE_PRIVATE).toString() 25 | } 26 | 27 | private fun cleanupTestDataDir(testDataDir: String) { 28 | File(testDataDir).deleteRecursively() 29 | } 30 | 31 | class LogProgress : Progress { 32 | private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java) 33 | 34 | override fun update(progress: Float, message: String?) { 35 | log.debug("Syncing...") 36 | } 37 | } 38 | 39 | private val descriptor = 40 | "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" 41 | 42 | private val databaseConfig = DatabaseConfig.Memory 43 | 44 | private val blockchainConfig = BlockchainConfig.Electrum( 45 | ElectrumConfig( 46 | "ssl://electrum.blockstream.info:60002", 47 | null, 48 | 5u, 49 | null, 50 | 100u 51 | ) 52 | ) 53 | 54 | @Test 55 | fun memoryWalletNewAddress() { 56 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 57 | val address = wallet.getAddress(AddressIndex.NEW).address 58 | assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address) 59 | } 60 | 61 | @Test 62 | fun memoryWalletSyncGetBalance() { 63 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 64 | val blockchain = Blockchain(blockchainConfig) 65 | wallet.sync(blockchain, LogProgress()) 66 | val balance: Balance = wallet.getBalance() 67 | assertTrue(balance.total > 0u) 68 | } 69 | 70 | @Test 71 | fun sqliteWalletSyncGetBalance() { 72 | val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite" 73 | val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir)) 74 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 75 | val blockchain = Blockchain(blockchainConfig) 76 | wallet.sync(blockchain, LogProgress()) 77 | val balance: Balance = wallet.getBalance() 78 | assertTrue(balance.total > 0u) 79 | cleanupTestDataDir(testDataDir) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /bdk-android/lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /bdk-android/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by `bdk-android`, and building the language bindings files. 3 | 4 | The plugin is applied to the `build.gradle.kts` file in `bdk-android` through the `plugins` block: 5 | ```kotlin 6 | // bdk-android 7 | plugins { 8 | id("org.bitcoindevkit.plugins.generate-android-bindings") 9 | } 10 | ``` 11 | 12 | It adds a series of tasks which are brought together into an aggregate task called `buildAndroidLib`. 13 | 14 | This aggregate task: 15 | 1. Builds the native libraries using `bdk-ffi` 16 | 2. Places them in the correct resource directories 17 | 3. Builds the bindings file 18 | -------------------------------------------------------------------------------- /bdk-android/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-gradle-plugin") 3 | `kotlin-dsl` 4 | } 5 | 6 | gradlePlugin { 7 | plugins { 8 | create("uniFfiAndroidBindings") { 9 | id = "org.bitcoindevkit.plugins.generate-android-bindings" 10 | implementationClass = "org.bitcoindevkit.plugins.UniFfiAndroidPlugin" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bdk-android/plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | mavenCentral() 4 | google() 5 | } 6 | } 7 | 8 | // include(":plugins") 9 | -------------------------------------------------------------------------------- /bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit.plugins 2 | 3 | 4 | val operatingSystem: OS = when { 5 | System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC 6 | System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX 7 | else -> OS.OTHER 8 | } 9 | 10 | enum class OS { 11 | MAC, 12 | LINUX, 13 | OTHER, 14 | } 15 | -------------------------------------------------------------------------------- /bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiAndroidPlugin.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit.plugins 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.api.tasks.Copy 6 | import org.gradle.api.tasks.Exec 7 | import org.gradle.kotlin.dsl.environment 8 | import org.gradle.kotlin.dsl.getValue 9 | import org.gradle.kotlin.dsl.provideDelegate 10 | import org.gradle.kotlin.dsl.register 11 | 12 | internal class UniFfiAndroidPlugin : Plugin { 13 | override fun apply(target: Project): Unit = target.run { 14 | val llvmArchPath = when (operatingSystem) { 15 | OS.MAC -> "darwin-x86_64" 16 | OS.LINUX -> "linux-x86_64" 17 | OS.OTHER -> throw Error("Cannot build Android library from current architecture") 18 | } 19 | 20 | // arm64-v8a is the most popular hardware architecture for Android 21 | val buildAndroidAarch64Binary by tasks.register("buildAndroidAarch64Binary") { 22 | 23 | workingDir("${projectDir}/../../bdk-ffi") 24 | val cargoArgs: MutableList = 25 | mutableListOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android") 26 | 27 | executable("cargo") 28 | args(cargoArgs) 29 | 30 | // if ANDROID_NDK_ROOT is not set then set it to github actions default 31 | if (System.getenv("ANDROID_NDK_ROOT") == null) { 32 | environment( 33 | Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle") 34 | ) 35 | } 36 | 37 | environment( 38 | // add build toolchain to PATH 39 | Pair("PATH", 40 | "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"), 41 | 42 | Pair("CFLAGS", "-D__ANDROID_API__=21"), 43 | Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"), 44 | Pair("CC", "aarch64-linux-android21-clang") 45 | ) 46 | 47 | doLast { 48 | println("Native library for bdk-android on aarch64 built successfully") 49 | } 50 | } 51 | 52 | // the x86_64 version of the library is mostly used by emulators 53 | val buildAndroidX86_64Binary by tasks.register("buildAndroidX86_64Binary") { 54 | 55 | workingDir("${project.projectDir}/../../bdk-ffi") 56 | val cargoArgs: MutableList = 57 | mutableListOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android") 58 | 59 | executable("cargo") 60 | args(cargoArgs) 61 | 62 | // if ANDROID_NDK_ROOT is not set then set it to github actions default 63 | if (System.getenv("ANDROID_NDK_ROOT") == null) { 64 | environment( 65 | Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle") 66 | ) 67 | } 68 | 69 | environment( 70 | // add build toolchain to PATH 71 | Pair("PATH", 72 | "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"), 73 | 74 | Pair("CFLAGS", "-D__ANDROID_API__=21"), 75 | Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"), 76 | Pair("CC", "x86_64-linux-android21-clang") 77 | ) 78 | 79 | doLast { 80 | println("Native library for bdk-android on x86_64 built successfully") 81 | } 82 | } 83 | 84 | // armeabi-v7a version of the library for older 32-bit Android hardware 85 | val buildAndroidArmv7Binary by tasks.register("buildAndroidArmv7Binary") { 86 | 87 | workingDir("${project.projectDir}/../../bdk-ffi") 88 | val cargoArgs: MutableList = 89 | mutableListOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi") 90 | 91 | executable("cargo") 92 | args(cargoArgs) 93 | 94 | // if ANDROID_NDK_ROOT is not set then set it to github actions default 95 | if (System.getenv("ANDROID_NDK_ROOT") == null) { 96 | environment( 97 | Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle") 98 | ) 99 | } 100 | 101 | environment( 102 | // add build toolchain to PATH 103 | Pair("PATH", 104 | "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"), 105 | 106 | Pair("CFLAGS", "-D__ANDROID_API__=21"), 107 | Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", 108 | "armv7a-linux-androideabi21-clang"), 109 | Pair("CC", "armv7a-linux-androideabi21-clang") 110 | ) 111 | 112 | doLast { 113 | println("Native library for bdk-android on armv7 built successfully") 114 | } 115 | } 116 | 117 | // move the native libs build by cargo from bdk-ffi/target//release/ 118 | // to their place in the bdk-android library 119 | // the task only copies the available binaries built using the buildAndroidBinary tasks 120 | val moveNativeAndroidLibs by tasks.register("moveNativeAndroidLibs") { 121 | 122 | dependsOn(buildAndroidAarch64Binary) 123 | 124 | into("${project.projectDir}/../lib/src/main/jniLibs/") 125 | 126 | into("arm64-v8a") { 127 | from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so") 128 | } 129 | 130 | into("x86_64") { 131 | from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so") 132 | } 133 | 134 | into("armeabi-v7a") { 135 | from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so") 136 | } 137 | 138 | doLast { 139 | println("Native binaries for Android moved to ./lib/src/main/jniLibs/") 140 | } 141 | } 142 | 143 | // generate the bindings using the bdk-ffi-bindgen tool located in the bdk-ffi submodule 144 | val generateAndroidBindings by tasks.register("generateAndroidBindings") { 145 | dependsOn(moveNativeAndroidLibs) 146 | 147 | workingDir("${project.projectDir}/../../bdk-ffi") 148 | executable("cargo") 149 | args( 150 | "run", 151 | "--package", 152 | "bdk-ffi-bindgen", 153 | "--", 154 | "--language", 155 | "kotlin", 156 | "--out-dir", 157 | "../bdk-android/lib/src/main/kotlin" 158 | ) 159 | 160 | doLast { 161 | println("Android bindings file successfully created") 162 | } 163 | } 164 | 165 | // create an aggregate task which will run the required tasks to build the Android libs in order 166 | // the task will also appear in the printout of the ./gradlew tasks task with group and description 167 | tasks.register("buildAndroidLib") { 168 | group = "Bitcoindevkit" 169 | description = "Aggregate task to build Android library" 170 | 171 | dependsOn( 172 | buildAndroidAarch64Binary, 173 | buildAndroidX86_64Binary, 174 | buildAndroidArmv7Binary, 175 | moveNativeAndroidLibs, 176 | generateAndroidBindings 177 | ) 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /bdk-android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "bdk-android" 2 | 3 | include(":lib") 4 | includeBuild("plugins") 5 | -------------------------------------------------------------------------------- /bdk-jvm/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("io.github.gradle-nexus.publish-plugin") version "1.1.0" 3 | } 4 | 5 | // These properties are required here so that the nexus publish-plugin 6 | // finds a staging profile with the correct group (group is otherwise set as "") 7 | // and knows whether to publish to a SNAPSHOT repository or not 8 | // https://github.com/gradle-nexus/publish-plugin#applying-the-plugin 9 | group = "org.bitcoindevkit" 10 | version = "0.12.0-SNAPSHOT" 11 | 12 | nexusPublishing { 13 | repositories { 14 | create("sonatype") { 15 | nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) 16 | snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) 17 | 18 | val ossrhUsername: String? by project 19 | val ossrhPassword: String? by project 20 | username.set(ossrhUsername) 21 | password.set(ossrhPassword) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /bdk-jvm/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536m 2 | android.enableJetifier=true 3 | kotlin.code.style=official 4 | -------------------------------------------------------------------------------- /bdk-jvm/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoindevkit/bdk-kotlin/12a7233c1cf789e257c9e80fb59caa0a53c14018/bdk-jvm/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /bdk-jvm/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /bdk-jvm/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /bdk-jvm/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /bdk-jvm/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.tasks.testing.logging.TestExceptionFormat.* 2 | import org.gradle.api.tasks.testing.logging.TestLogEvent.* 3 | 4 | plugins { 5 | id("org.jetbrains.kotlin.jvm") version "1.6.10" 6 | id("java-library") 7 | id("maven-publish") 8 | id("signing") 9 | 10 | // Custom plugin to generate the native libs and bindings file 11 | id("org.bitcoindevkit.plugins.generate-jvm-bindings") 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | java { 19 | sourceCompatibility = JavaVersion.VERSION_1_8 20 | targetCompatibility = JavaVersion.VERSION_1_8 21 | withSourcesJar() 22 | withJavadocJar() 23 | } 24 | 25 | tasks.withType { 26 | useJUnitPlatform() 27 | 28 | testLogging { 29 | events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR) 30 | exceptionFormat = FULL 31 | showExceptions = true 32 | showCauses = true 33 | showStackTraces = true 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation(platform("org.jetbrains.kotlin:kotlin-bom")) 39 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") 40 | implementation("net.java.dev.jna:jna:5.8.0") 41 | api("org.slf4j:slf4j-api:1.7.30") 42 | testImplementation("junit:junit:4.13.2") 43 | testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2") 44 | testImplementation("ch.qos.logback:logback-classic:1.2.3") 45 | testImplementation("ch.qos.logback:logback-core:1.2.3") 46 | } 47 | 48 | afterEvaluate { 49 | publishing { 50 | publications { 51 | create("maven") { 52 | groupId = "org.bitcoindevkit" 53 | artifactId = "bdk-jvm" 54 | version = "0.12.0-SNAPSHOT" 55 | 56 | from(components["java"]) 57 | pom { 58 | name.set("bdk-jvm") 59 | description.set("Bitcoin Dev Kit Kotlin language bindings.") 60 | url.set("https://bitcoindevkit.org") 61 | licenses { 62 | license { 63 | name.set("APACHE 2.0") 64 | url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE") 65 | } 66 | license { 67 | name.set("MIT") 68 | url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT") 69 | } 70 | } 71 | developers { 72 | developer { 73 | id.set("notmandatory") 74 | name.set("Steve Myers") 75 | email.set("notmandatory@noreply.github.org") 76 | } 77 | developer { 78 | id.set("artfuldev") 79 | name.set("Sudarsan Balaji") 80 | email.set("sudarsan.balaji@artfuldev.com") 81 | } 82 | } 83 | scm { 84 | connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git") 85 | developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git") 86 | url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master") 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | 94 | signing { 95 | val signingKeyId: String? by project 96 | val signingKey: String? by project 97 | val signingPassword: String? by project 98 | useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) 99 | sign(publishing.publications) 100 | } 101 | -------------------------------------------------------------------------------- /bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit 2 | 3 | import org.junit.Assert.* 4 | import org.junit.Test 5 | import org.slf4j.Logger 6 | import org.slf4j.LoggerFactory 7 | import java.io.File 8 | import java.nio.file.Files 9 | 10 | /** 11 | * Library test, which will execute on linux host. 12 | */ 13 | class JvmLibTest { 14 | 15 | private fun getTestDataDir(): String { 16 | return Files.createTempDirectory("bdk-test").toString() 17 | } 18 | 19 | private fun cleanupTestDataDir(testDataDir: String) { 20 | File(testDataDir).deleteRecursively() 21 | } 22 | 23 | class LogProgress : Progress { 24 | private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java) 25 | 26 | override fun update(progress: Float, message: String?) { 27 | log.debug("Syncing...") 28 | } 29 | } 30 | 31 | private val descriptor = 32 | "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" 33 | 34 | private val databaseConfig = DatabaseConfig.Memory 35 | 36 | private val blockchainConfig = BlockchainConfig.Electrum( 37 | ElectrumConfig( 38 | "ssl://electrum.blockstream.info:60002", 39 | null, 40 | 5u, 41 | null, 42 | 100u 43 | ) 44 | ) 45 | 46 | @Test 47 | fun memoryWalletNewAddress() { 48 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 49 | val address = wallet.getAddress(AddressIndex.NEW).address 50 | assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address) 51 | } 52 | 53 | @Test 54 | fun memoryWalletSyncGetBalance() { 55 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 56 | val blockchain = Blockchain(blockchainConfig) 57 | wallet.sync(blockchain, LogProgress()) 58 | val balance: Balance = wallet.getBalance() 59 | assertTrue(balance.total > 0u) 60 | } 61 | 62 | @Test 63 | fun sqliteWalletSyncGetBalance() { 64 | val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite" 65 | val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir)) 66 | val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig) 67 | val blockchain = Blockchain(blockchainConfig) 68 | wallet.sync(blockchain, LogProgress()) 69 | val balance: Balance = wallet.getBalance() 70 | assertTrue(balance.total > 0u) 71 | cleanupTestDataDir(testDataDir) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /bdk-jvm/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by bdk-jvm, and building the language bindings files. 3 | 4 | The plugin is applied to the `build.gradle.kts` file through the `plugins` block: 5 | ```kotlin 6 | plugins { 7 | id("org.bitcoindevkit.plugin.generate-jvm-bindings") 8 | } 9 | ``` 10 | 11 | The plugin adds a series of tasks which are brought together into an aggregate task called `buildJvmLib` for `bdk-jvm`. 12 | 13 | This aggregate task: 14 | 1. Builds the native library(ies) using `bdk-ffi` 15 | 2. Places it in the correct resource directory 16 | 3. Builds the bindings file 17 | -------------------------------------------------------------------------------- /bdk-jvm/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-gradle-plugin") 3 | `kotlin-dsl` 4 | } 5 | 6 | gradlePlugin { 7 | plugins { 8 | create("uniFfiJvmBindings") { 9 | id = "org.bitcoindevkit.plugins.generate-jvm-bindings" 10 | implementationClass = "org.bitcoindevkit.plugins.UniFfiJvmPlugin" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bdk-jvm/plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories { 3 | mavenCentral() 4 | google() 5 | } 6 | } 7 | 8 | // include(":plugins") 9 | -------------------------------------------------------------------------------- /bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit.plugins 2 | 3 | 4 | val operatingSystem: OS = when { 5 | System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC 6 | System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX 7 | else -> OS.OTHER 8 | } 9 | 10 | enum class OS { 11 | MAC, 12 | LINUX, 13 | OTHER, 14 | } 15 | -------------------------------------------------------------------------------- /bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiJvmPlugin.kt: -------------------------------------------------------------------------------- 1 | package org.bitcoindevkit.plugins 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | import org.gradle.api.tasks.Exec 7 | import org.gradle.kotlin.dsl.getValue 8 | import org.gradle.kotlin.dsl.provideDelegate 9 | import org.gradle.kotlin.dsl.register 10 | 11 | internal class UniFfiJvmPlugin : Plugin { 12 | override fun apply(target: Project): Unit = target.run { 13 | 14 | // register a task called buildJvmBinaries which will run something like 15 | // cargo build --release --target aarch64-apple-darwin 16 | val buildJvmBinaries by tasks.register("buildJvmBinaries") { 17 | if (operatingSystem == OS.MAC) { 18 | exec { 19 | workingDir("${project.projectDir}/../../bdk-ffi") 20 | executable("cargo") 21 | val cargoArgs: List = listOf("build", "--profile", "release-smaller", "--target", "x86_64-apple-darwin") 22 | args(cargoArgs) 23 | } 24 | exec { 25 | workingDir("${project.projectDir}/../../bdk-ffi") 26 | executable("cargo") 27 | val cargoArgs: List = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin") 28 | args(cargoArgs) 29 | } 30 | } else if(operatingSystem == OS.LINUX) { 31 | exec { 32 | workingDir("${project.projectDir}/../../bdk-ffi") 33 | executable("cargo") 34 | val cargoArgs: List = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu") 35 | args(cargoArgs) 36 | } 37 | } 38 | } 39 | 40 | // move the native libs build by cargo from bdk-ffi/target/.../release/ 41 | // to their place in the bdk-jvm library 42 | val moveNativeJvmLibs by tasks.register("moveNativeJvmLibs") { 43 | 44 | // dependsOn(buildJvmBinaryX86_64MacOS, buildJvmBinaryAarch64MacOS, buildJvmBinaryLinux) 45 | dependsOn(buildJvmBinaries) 46 | 47 | data class CopyMetadata(val targetDir: String, val resDir: String, val ext: String) 48 | val libsToCopy: MutableList = mutableListOf() 49 | 50 | if (operatingSystem == OS.MAC) { 51 | libsToCopy.add( 52 | CopyMetadata( 53 | targetDir = "aarch64-apple-darwin", 54 | resDir = "darwin-aarch64", 55 | ext = "dylib" 56 | ) 57 | ) 58 | libsToCopy.add( 59 | CopyMetadata( 60 | targetDir = "x86_64-apple-darwin", 61 | resDir = "darwin-x86-64", 62 | ext = "dylib" 63 | ) 64 | ) 65 | } else if (operatingSystem == OS.LINUX) { 66 | libsToCopy.add( 67 | CopyMetadata( 68 | targetDir = "x86_64-unknown-linux-gnu", 69 | resDir = "linux-x86-64", 70 | ext = "so" 71 | ) 72 | ) 73 | } 74 | 75 | libsToCopy.forEach { 76 | doFirst { 77 | copy { 78 | with(it) { 79 | from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/libbdkffi.${this.ext}") 80 | into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/") 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | // generate the bindings using the bdk-ffi-bindgen tool created in the bdk-ffi submodule 88 | val generateJvmBindings by tasks.register("generateJvmBindings") { 89 | 90 | dependsOn(moveNativeJvmLibs) 91 | 92 | workingDir("${project.projectDir}/../../bdk-ffi") 93 | executable("cargo") 94 | args( 95 | "run", 96 | "--package", 97 | "bdk-ffi-bindgen", 98 | "--", 99 | "--language", 100 | "kotlin", 101 | "--out-dir", 102 | "../bdk-jvm/lib/src/main/kotlin" 103 | ) 104 | 105 | doLast { 106 | println("JVM bindings file successfully created") 107 | } 108 | } 109 | 110 | // we need an aggregate task which will run the 3 required tasks to build the JVM libs in order 111 | // the task will also appear in the printout of the ./gradlew tasks task with a group and description 112 | tasks.register("buildJvmLib") { 113 | group = "Bitcoindevkit" 114 | description = "Aggregate task to build JVM library" 115 | 116 | dependsOn( 117 | buildJvmBinaries, 118 | moveNativeJvmLibs, 119 | generateJvmBindings 120 | ) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /bdk-jvm/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "bdk-jvm" 2 | 3 | include(":lib") 4 | includeBuild("plugins") 5 | --------------------------------------------------------------------------------