├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── btcli4j-gen.sh ├── btcli4j.sh ├── build.gradle.kts ├── changelog.json ├── coffee.yml ├── compile_commands.json ├── devlibs └── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src └── main ├── kotlin └── io │ └── vincenzopalazzo │ └── btcli4j │ ├── App.kt │ ├── Plugin.kt │ ├── control │ ├── MediationMethod.kt │ ├── checkchain │ │ ├── ChainOfResponsibilityCheck.kt │ │ ├── CheckResult.kt │ │ └── checks │ │ │ ├── BlockNotFoundCheck.kt │ │ │ ├── CheckHandler.kt │ │ │ ├── GetUtxoInvalidFormat.kt │ │ │ └── TransactionCheck.kt │ └── commands │ │ ├── ICommand.kt │ │ ├── btcprune │ │ ├── EstimateFeeBtc.kt │ │ ├── GetChainInfoBtc.kt │ │ ├── GetRawBlockByHeightBtc.kt │ │ ├── GetUtxOutBtc.kt │ │ └── SendRawTransactionBtc.kt │ │ └── esplora │ │ ├── EstimateFeeCommand.kt │ │ ├── GetChainInfoCommand.kt │ │ ├── GetRawBlockByHeightCommand.kt │ │ ├── GetUtxOutCommand.kt │ │ └── SendRawTransactionCommand.kt │ ├── model │ ├── bitcoin │ │ ├── BlockchainInfoBitcoin.kt │ │ ├── EstimateFeeBitcoin.kt │ │ ├── MemPoolInfo.kt │ │ └── UTXOBitcoin.kt │ └── esplora │ │ ├── BTCTransactionInputModel.kt │ │ ├── BTCTransactionModel.kt │ │ ├── BTCTransactionOutputModel.kt │ │ ├── EstimateFeeModel.kt │ │ ├── StatusOnChain.kt │ │ ├── StatusUTXOModel.kt │ │ └── TransactionStatusModel.kt │ └── util │ ├── HttpRequestFactory.kt │ ├── JSONConverter.kt │ ├── OptionsManager.kt │ ├── PluginManager.kt │ └── typeadapter │ └── EstimateFeeTypeAdapter.kt └── resources └── logback-test.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | disabled_rules=import-ordering 3 | 4 | # Note that in this case 'import-ordering' rule will be active and 'indent' will be disabled 5 | [api/*.{kt,kts}] 6 | disabled_rules=indent 7 | 8 | # Comma-separated list of rules to disable (Since 0.34.0) 9 | # Note that rules in any ruleset other than the standard ruleset will need to be prefixed 10 | # by the ruleset identifier. 11 | disabled_rules=no-wildcard-imports,experimental:annotation,my-custom-ruleset 12 | 13 | # Defines the imports layout. The layout can be composed by the following symbols: 14 | # "*" - wildcard. There must be at least one entry of a single wildcard to match all other imports. Matches anything after a specified symbol/import as well. 15 | # "|" - blank line. Supports only single blank lines between imports. No blank line is allowed in the beginning or end of the layout. 16 | # "^" - alias import, e.g. "^android.*" will match all android alias imports, "^" will match all other alias imports. 17 | # import paths - these can be full paths, e.g. "java.util.List.*" as well as wildcard paths, e.g. "kotlin.**" 18 | # Examples (we use ij_kotlin_imports_layout to set an imports layout for both ktlint and IDEA via a single property): 19 | ij_kotlin_imports_layout=* # alphabetical with capital letters before lower case letters (e.g. Z before a), no blank lines 20 | ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ # default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list 21 | ij_kotlin_imports_layout=android.**,|,^org.junit.**,kotlin.io.Closeable.*,|,*,^ # custom imports layout 22 | 23 | # According to https://kotlinlang.org/docs/reference/coding-conventions.html#names-for-test-methods it is acceptable to write method names 24 | # in natural language. When using natural language, the description tends to be longer. Allow lines containing an identifier between 25 | # backticks to be longer than the maximum line length. (Since 0.41.0) 26 | [**/test/**.kt] 27 | ktlint_ignore_back_ticked_identifier=true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | include: 11 | - { JDK: 1.8 } 12 | - { JDK: 11 } 13 | - { JDK: 14 } 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Set up JDK ${{ matrix.JDK }} 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: ${{ matrix.JDK }} 20 | - name: Check Kotling format 21 | run: | 22 | ./gradlew lintKotlin 23 | - name: Build with Gradle 24 | run: | 25 | ./gradlew createRunnableScript 26 | cat btcli4j-gen.sh -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build relase for different JDK 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - { JDK: 1.8 } 16 | - { JDK: 11 } 17 | - { JDK: 14 } 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: Set up JDK ${{ matrix.JDK }} and upload jar 21 | uses: actions/setup-java@v1 22 | with: 23 | java-version: ${{ matrix.JDK }} 24 | - name: Build with Gradle 25 | run: | 26 | ./gradlew createRunnableScript 27 | - uses: actions/upload-artifact@v2 28 | with: 29 | name: btcli4j-all-${{ matrix.JDK }} 30 | path: build/libs/btcli4j-all.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | # Ignore Gradle build output directory 4 | build 5 | .idea/ 6 | **/*.jar 7 | **/*.sh -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.0.7 2 | 3 | ## Fixed 4 | - fix the estimation of the fee in pruning mode ([commit](https://github.com/clightning4j/btcli4j/commit/76d9823120cba399591766120a569bbb63551a15)). @vincenzopalazzo 29-03-2023 5 | - coffe script ([commit](https://github.com/clightning4j/btcli4j/commit/de360fa8e24e31e693c8636a32eac7ce07e9cea8)). @vincenzopalazzo 08-03-2023 6 | - coffe script ([commit](https://github.com/clightning4j/btcli4j/commit/c233640b005600dd4f9b41c9b49a81b8e1733a9c)). @vincenzopalazzo 08-03-2023 7 | 8 | ## Added 9 | - support coffee manifest ([commit](https://github.com/clightning4j/btcli4j/commit/e006d838477b149d650c36eb1e065cc40813bd77)). @vincenzopalazzo 08-03-2023 10 | 11 | 12 | # v0.0.6 13 | 14 | ## Fixed 15 | - fix the possibility to disable the cln proxy ([commit](https://github.com/clightning4j/btcli4j/commit/99046cd04609a8a0c9dfcbb56e1a0de57150a9aa)). @vincenzopalazzo 28-01-2023 16 | 17 | 18 | # v0.0.5 19 | 20 | ## Fixed 21 | - upgrade the java library with the upstream ([commit](https://github.com/clightning4j/btcli4j/commit/d04d0a03a54d24fcaedbd1021eba2ce566dffe36)). @vincenzopalazzo 14-01-2023 22 | 23 | 24 | # v0.0.4 25 | 26 | ## Fixed 27 | - send raw transaction cln response ([commit](https://github.com/clightning4j/btcli4j/commit/7e39245cf4c1b0c623a361550dffb2d3a76e0e69)). @vincenzopalazzo 16-11-2022 28 | - use 6 block of estimation fee ([commit](https://github.com/clightning4j/btcli4j/commit/d2bca9236b17db03003c1d6fbc50798d47db5825)). @vincenzopalazzo 16-11-2022 29 | - if a error is throws from bitcoind return the error to cln ([commit](https://github.com/clightning4j/btcli4j/commit/7ab269a2407e0d721d0fce2d66db29d9cb122454)). @vincenzopalazzo 16-11-2022 30 | - Update gitignore. ([commit](https://github.com/clightning4j/btcli4j/commit/d281e041159ce500dbec237c3b5d26c5d72526d9)). @swaptr 28-05-2022 31 | 32 | 33 | # v0.0.3 34 | 35 | ## Fixed 36 | - if a error is throws from bitcoind return the error to cln ([commit](https://github.com/clightning4j/btcli4j/commit/7ab269a2407e0d721d0fce2d66db29d9cb122454)). @vincenzopalazzo 16-11-2022 37 | - Update gitignore. ([commit](https://github.com/clightning4j/btcli4j/commit/d281e041159ce500dbec237c3b5d26c5d72526d9)). @swaptr 28-05-2022 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | Contributions are very welcome. When contributing code, please follow these 4 | simple guidelines. 5 | 6 | * Make sure you run `.gradlew googleJavaFormat`. 7 | * Run the tests with `docker-compose up --build` and Github action it is happy. 8 | * Don't add any new dependencies. 9 | * Write properly formatted git commits (see below). 10 | * If you don't know where to start, look to the good first issue, or [open a discussion](https://github.com/clightning4j/JRPClightning/discussions) 11 | 12 | Git commits 13 | ----------- 14 | A properly formed git commit subject line should always be able to complete the 15 | following sentence: 16 | 17 | If applied, this commit will _____ 18 | 19 | For example, the following message is well formed: 20 | 21 | Add support for .gif files 22 | 23 | In addition, it should be capitalized and *must not* include a period. 24 | 25 | When it comes to formatting, here's a model git commit message[1]: 26 | 27 | Capitalized, short (50 chars or less) summary 28 | 29 | More detailed explanatory text, if necessary. Wrap it to about 72 30 | characters or so. In some contexts, the first line is treated as the 31 | subject of an email and the rest of the text as the body. The blank 32 | line separating the summary from the body is critical (unless you omit 33 | the body entirely); tools like rebase can get confused if you run the 34 | two together. 35 | 36 | Write your commit message in the imperative: "Fix bug" and not "Fixed bug" 37 | or "Fixes bug." This convention matches up with commit messages generated 38 | by commands like git merge and git revert. 39 | 40 | Further paragraphs come after blank lines. 41 | 42 | - Bullet points are okay, too. 43 | 44 | - Typically a hyphen or asterisk is used for the bullet, followed by a 45 | single space, with blank lines in between, but conventions vary here. 46 | 47 | - Use a hanging indent. 48 | 49 | --- 50 | 51 | [1]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | C-lightning plugin to override Bitcoin backend plugin. 294 | Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Btcli4j 2 | 3 | ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/clightning4j/btcli4j/Java%20CI?style=flat-square) 4 | ![Esplora support](https://img.shields.io/badge/esplora-supported-gren?style=flat-square) 5 | ![Pruning mode](https://img.shields.io/badge/prune-supported-gren?style=flat-square) 6 | 7 | It is a [c-lightning](https://lightning.readthedocs.io/index.html) plugin to override Bitcoin backend plugin with [esplora](https://github.com/Blockstream/esplora) 8 | powered by [Blockstream](https://blockstream.com/). 9 | 10 | But this plugin is designed make the process to run c-lightning with bitcoind in pruning mode, the idea of this plugin was 11 | described inside this [issue by lightningd](https://github.com/lightningd/plugins/issues/112). 12 | 13 | It is designed with mediator patter and each command inside the mediator can use esplora or bitcoind or a both in some cases. 14 | 15 | So, the plugin will support the complete backend with esplora (only) and the complete backend only with bitcoind or a join of the two option! 16 | 17 | ## Network supported 18 | 19 | - [X] Bitcoin (Mainet (Be cautious) and Testnet) 20 | - [X] Liquid 21 | - [X] Bitcoin Core pruning mode (Mainet (Be cautious) and Testnet) 22 | - [X] Bitcoin Core remote call (have bitcoin in another location of your lightningd node) (Mainet (Be cautious) and Testnet) 23 | - [ ] Litecoin 24 | - [ ] Liquid 25 | 26 | ## Status 27 | 28 | The plugin is in Beta stage, this mean that is waiting for the User Review and testing, good tested on bitcoin testnet 29 | but not on mainet, because I have put inside only small amount of found that available at the moment, if you want support 30 | the project consider to look in the section [Support]() 31 | 32 | If you want test it all feedback are welcome! Feel free to open an issue or a PR or in addition, you can send 33 | email to [vincenzopalazzodev@gmail.com](mailito://vincenzopalazzodev@gmail.com) 34 | 35 | ## Install 36 | Java produces a jar and c-lightning needs a bash script to run it! 37 | The gradle script compiles the plugin and generate a bash script with the command `./gradlew createRunnableScript` 38 | 39 | After the gradle process, you have the jar inside the `build/libs/btcli4j-all.jar` and the script `btcli4j-gen.sh` 40 | in the root directory of the project. 41 | 42 | Now you can put the gradle script inside the c-lightning plugin directory 43 | 44 | ### How bash script look like 45 | 46 | The contains only the command to run the jar with java, in my cases the script contains the following result> 47 | 48 | ```bash 49 | #!/bin/bash 50 | /usr/lib/jvm/jdk-13.0.2/bin/java -jar /home/vincent/Github/btcli4j/build/libs/btcli4j-all.jar 51 | ``` 52 | 53 | In this case, you can move this bash script inside your `./lightning/plugins` the directory or you can add the plugin to the file conf 54 | with `plugin=/PATH/bash/file` or use the command line `--plugin=/path/bash/file`. 55 | 56 | ## Plugin parameter 57 | 58 | - btcli4j-endpoint: A string option that is the end point url,by default it is the [esplora](https://github.com/Blockstream/esplora/blob/master/API.md) end point, 59 | - it is the end point it is different the proxy with tor is lost from the automatic configuration of the plugin 60 | - btcli4j-waiting: A integer that is the base waiting time where the plugin will apply the increase waiting time in case of error with the end point. 61 | - btcli4j-proxy: Indicate the proxy address, by default is: "127.0.0.1:9050". 62 | - btcli4j-proxy-disabled: A flag option, it helps the user to disabled the cln proxy setting if enabled. 63 | - btcli4j-pruned: A flag option that tell to the plugin to ran in pruning mode 64 | - bitcoin-rpcurl: A string option that is the url of Bitcoin RPC interface, usually `http://127.0.0.1:8332` for mainet and `http://127.0.0.1:18332` for testnet 65 | - bitcoin-rpcuser: A string option that is the user to make the login in Bitcoin core 66 | - bitcoin-rpcpassword: A string option that is the password to make the login in Bitcoin core 67 | 68 | P.S: some of Bitcoin propriety are the same of the C-lightning propriety, these mean that yuo need to redefine only a ``bitcoin-rpcurl`` propriety, and put 69 | at the startup of the lightning node the flag `btcli4j-pruned` 70 | 71 | ### Esplora API config 72 | 73 | A complete example to run with Esplora API is reported below 74 | 75 | ```bash 76 | lightningd --disable-plugin bcli 77 | ``` 78 | 79 | The command above, run the lightningd with the lightnind configuration, this mean that you don't need to add some 80 | additional configuration to the command line. 81 | 82 | However, you can use the command line to customize the plugin behaviors. 83 | 84 | ### Bitcoin Pruning mode config 85 | 86 | ```bash 87 | lightningd --disable-plugin bcli --btcli4j-pruned 88 | ``` 89 | 90 | With the following clightning config 91 | 92 | ```bash 93 | ... 94 | bitcoin-rpcuser=user 95 | bitcoin-rpcpassword=pass 96 | bitcoin-rpcurl=http://127.0.0.1:18332 97 | .... 98 | ``` 99 | 100 | ## Code Style 101 | [![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/) 102 | 103 | > We live in a world where robots can drive a car, so we shouldn't just write code, we should write elegant code. 104 | 105 | This repository use [ktlint](https://github.com/pinterest/ktlint) to maintains the code of the repository elegant, so 106 | before submit the code check the Kotlin format with the following command on the root of the directory 107 | 108 | ```bash 109 | ./gradlew formatKotlin 110 | ``` 111 | 112 | ## Built with 113 | 114 | - [JRPClightning](https://github.com/vincenzopalazzo/JRPClightning) 115 | - [lite-bitcoin-rpc](https://github.com/clightning4j/lite-bitcoin-rpc) 116 | 117 | ## Support 118 | If you like the library and want to support it, please considerer to donate with the following system 119 | 120 | 121 | - [liberapay.com/vincenzopalazzo](https://liberapay.com/vincenzopalazzo) 122 | - [3BQ8qbn8hLdmBKEjt1Hj1Z6SiDsnjJurfU](bitcoin:3BQ8qbn8hLdmBKEjt1Hj1Z6SiDsnjJurfU) 123 | - [Github support](https://github.com/sponsors/vincenzopalazzo) 124 | 125 | ## License 126 | 127 |
128 | 129 |
130 | 131 | It is a c-lightning plugin to override Bitcoin backend plugin with esplora. 132 | 133 | Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 134 | 135 | This program is free software; you can redistribute it and/or modify 136 | it under the terms of the GNU General Public License as published by 137 | the Free Software Foundation; either version 2 of the License. 138 | 139 | This program is distributed in the hope that it will be useful, 140 | but WITHOUT ANY WARRANTY; without even the implied warranty of 141 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 142 | GNU General Public License for more details. 143 | 144 | You should have received a copy of the GNU General Public License along 145 | with this program; if not, write to the Free Software Foundation, Inc., 146 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 147 | -------------------------------------------------------------------------------- /btcli4j-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script generated from gradle! By clightning4j 3 | 4 | /usr/lib/jvm/java-20-openjdk/bin/java -jar /home/vincent/Github/clightning4j/btcli4j/build/libs/btcli4j-all.jar -------------------------------------------------------------------------------- /btcli4j.sh: -------------------------------------------------------------------------------- 1 | # C-lightning plugin to create a small QR code when lightningd made somethings 2 | # Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 3 | 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | /usr/lib/jvm/jdk-13.0.2/bin/java -jar /home/vincent/Github/btcli4j/build/libs/btcli4j-all.jar -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.jetbrains.kotlin.jvm") version "1.5.30" 3 | id("org.jmailen.kotlinter") version "3.3.0" 4 | //TODO: https://github.com/mike-neck/graalvm-native-image-plugin 5 | //id("org.mikeneck.graalvm-native-image") version "1.3.0" 6 | application 7 | maven 8 | } 9 | 10 | configurations.all { 11 | resolutionStrategy.cacheDynamicVersionsFor(0, "seconds") 12 | resolutionStrategy.cacheChangingModulesFor(0, "seconds") 13 | } 14 | 15 | repositories { 16 | jcenter() 17 | mavenCentral() 18 | maven("https://oss.sonatype.org/content/repositories/snapshots") 19 | } 20 | 21 | dependencies { 22 | implementation(platform("org.jetbrains.kotlin:kotlin-bom")) 23 | implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1")) 24 | 25 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 26 | implementation("com.google.code.gson:gson:2.8.6") 27 | implementation("com.squareup.okhttp3:okhttp") 28 | implementation("com.squareup.okhttp3:logging-interceptor") 29 | implementation("io.github.clightning4j:jrpclightning:0.2.5-SNAPSHOT") 30 | implementation("io.github.clightning4j:lite-bitcoin-rpc:0.0.1-rc2-SNAPSHOT") 31 | 32 | //Developing library 33 | /*api(fileTree("${project.projectDir}/devlibs") { 34 | include("jrpclightning-0.1.9-SNAPSHOT-with-dependencies.jar") 35 | }) */ 36 | testImplementation("org.jetbrains.kotlin:kotlin-test") 37 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit") 38 | } 39 | 40 | application { 41 | mainClass.set("io.vincenzopalazzo.btcli4j.AppKt") 42 | } 43 | 44 | tasks { 45 | register("fatJar", Jar::class.java) { 46 | archiveClassifier.set("all") 47 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 48 | manifest { 49 | attributes("Main-Class" to application.mainClass) 50 | } 51 | from( 52 | configurations.runtimeClasspath.get() 53 | .onEach { println("add from dependencies: ${it.name}") } 54 | .map { if (it.isDirectory) it else zipTree(it) } 55 | ) 56 | val sourcesMain = sourceSets.main.get() 57 | sourcesMain.allSource.forEach { println("add from sources: ${it.name}") } 58 | from(sourcesMain.output) 59 | } 60 | 61 | register("createRunnableScript") { 62 | dependsOn("fatJar") 63 | file("$projectDir/${project.name}-gen.sh").createNewFile() 64 | file("$projectDir/${project.name}-gen.sh").writeText( 65 | """ 66 | #!/bin/bash 67 | # Script generated from gradle! By clightning4j 68 | 69 | ${System.getProperties().getProperty("java.home")}/bin/java -jar ${project.buildDir.absolutePath}/libs/${project.name}-all.jar 70 | """.trimIndent() 71 | ) 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /changelog.json: -------------------------------------------------------------------------------- 1 | { 2 | "package_name": "btcli4j", 3 | "version": "v0.0.7", 4 | "api": { 5 | "name": "github", 6 | "repository": "clightning4j/btcli4j", 7 | "branch": "master" 8 | }, 9 | "generation_method": { 10 | "name": "semver-v2", 11 | "header_filter": false 12 | }, 13 | "serialization_method": { 14 | "name": "md" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /coffee.yml: -------------------------------------------------------------------------------- 1 | --- 2 | plugin: 3 | name: btcli4j 4 | version: 0.0.1 5 | lang: java 6 | install: | 7 | sh -C ./gradlew createRunnableScript 8 | main: btcli4j-gen.sh 9 | -------------------------------------------------------------------------------- /compile_commands.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /devlibs/README.md: -------------------------------------------------------------------------------- 1 | ## Dev Sandbox 2 | 3 | If you want compile the plugin with the last version of [JRPCLightning](https://github.com/clightning4j/JRPClightning) 4 | put here the jar with all dependencies -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clightning4j/btcli4j/8798c3153c0a9040126857c3c76b4233479c2d16/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 init 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 init 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 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.4.1/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = "btcli4j" -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/App.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j 20 | 21 | /** 22 | * @author https://github.com/vincenzopalazzo 23 | */ 24 | fun main() { 25 | val btcCli4j = Plugin() 26 | btcCli4j.start() 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/Plugin.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j 20 | 21 | import io.vincenzopalazzo.btcli4j.control.MediationMethod 22 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 23 | import io.vincenzopalazzo.btcli4j.util.OptionsManager 24 | import io.vincenzopalazzo.btcli4j.util.PluginManager 25 | import jrpc.clightning.annotation.PluginOption 26 | import jrpc.clightning.annotation.RPCMethod 27 | import jrpc.clightning.plugins.CLightningPlugin 28 | import jrpc.clightning.plugins.log.PluginLog 29 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 30 | 31 | /** 32 | * @author https://github.com/vincenzopalazzo 33 | */ 34 | class Plugin : CLightningPlugin() { 35 | 36 | @PluginOption( 37 | name = "btcli4j-endpoint", 38 | description = "This option give information on rest end point url, by default it is the esplora url. " + 39 | "If you specify this URL you are losing the autoconfiguration of the tor proxy in case of proxy enabled with lightningd.", 40 | defValue = "https://blockstream.info", 41 | typeValue = "string" 42 | ) 43 | private var endpointUrl: String = "https://blockstream.info" 44 | 45 | @PluginOption( 46 | name = "btcli4j-waiting", 47 | description = "This option give information on the waiting time in case of error with the endpoint connection. " + 48 | "In case of failure this value is the base value where the plugin start to use the increase waiting time rules.", 49 | defValue = "60000", 50 | typeValue = "int" 51 | ) 52 | private var waitingTime: Int = 60000 53 | 54 | @PluginOption( 55 | name = "btcli4j-proxy", 56 | description = "This option give information on proxy enabled, by default set on tor proxy", 57 | defValue = "127.0.0.1:9050", 58 | typeValue = "string" 59 | ) 60 | private var proxy: String = "127.0.0.1:9050" 61 | 62 | @PluginOption( 63 | name = "btcli4j-proxy-disabled", 64 | description = "This option give disable the proxy inside plugin. By default is false and use the cln settings", 65 | defValue = "false", 66 | typeValue = "flag" 67 | ) 68 | private var proxyDisabled: Boolean = false 69 | 70 | @PluginOption( 71 | name = "bitcoin-rpcpassword", 72 | description = "Bitcoin RPC password", 73 | defValue = "", 74 | typeValue = "string" 75 | ) 76 | private var bitcoinRpcPass: String = "" 77 | 78 | @PluginOption( 79 | name = "bitcoin-rpcuser", 80 | description = "Bitcoin RPC user", 81 | defValue = "", 82 | typeValue = "string" 83 | ) 84 | private var bitcoinRpcUser: String = "" 85 | 86 | @PluginOption( 87 | name = "bitcoin-rpcurl", 88 | description = "Base URL Bitcoin RPC", 89 | defValue = "", 90 | typeValue = "string" 91 | ) 92 | private var bitcoinBaseUrl: String = "" 93 | 94 | @PluginOption( 95 | name = "btcli4j-pruned", 96 | description = "Tell to BTC4J plugin to use the pruned mode", 97 | defValue = "false", 98 | typeValue = "flag" 99 | ) 100 | private var prunedMode = false 101 | 102 | private var pluginInit = false 103 | 104 | @RPCMethod(name = "getchaininfo", description = "getchaininfo to fetch the data from blockstream.info") 105 | fun getChainInfo(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 106 | this.configurePluginInit() 107 | MediationMethod.runCommand("getchaininfo", plugin, CLightningJsonObject(request["params"].asJsonObject), response) 108 | } 109 | 110 | @RPCMethod(name = "estimatefees", description = "estimatefees to fetch the feed estimation from blockstream.info") 111 | fun estimateFees(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 112 | MediationMethod.runCommand("estimatefees", plugin, CLightningJsonObject(request["params"].asJsonObject), response) 113 | } 114 | 115 | @RPCMethod(name = "getrawblockbyheight", description = "getrawblockbyheight to fetch actual blockchain height from blockstream.info") 116 | fun getRawBlockByHeight(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 117 | MediationMethod.runCommand("getrawblockbyheight", plugin, CLightningJsonObject(request["params"].asJsonObject), response) 118 | } 119 | 120 | @RPCMethod(name = "getutxout", description = "getutxout to fetch a utx with {txid} and {vout} from blockstream.info") 121 | fun getUtxOut(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 122 | MediationMethod.runCommand("getutxout", plugin, CLightningJsonObject(request["params"].asJsonObject), response) 123 | } 124 | 125 | @RPCMethod(name = "sendrawtransaction", description = "sendrawtransaction to publish a new transaction with blockstream.info") 126 | fun sendRawTransaction(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 127 | MediationMethod.runCommand("sendrawtransaction", plugin, CLightningJsonObject(request["params"].asJsonObject), response) 128 | } 129 | 130 | private fun configurePluginInit() { 131 | // TODO: The plugin have some proxy enabled and proxy url propriety to give the possibility to the user 132 | // to specify a custom proxy only of the http client used by the plugin. Implement this view. 133 | val optionsManager = OptionsManager(endpointUrl, waitingTime, null, !this.proxyDisabled, proxy) 134 | if (!pluginInit && configs.isProxyEnabled && !this.proxyDisabled) { 135 | val proxyIp = configs.proxy.address 136 | val proxyPort = configs.proxy.port 137 | val torVersion = when (configs.proxy.type) { 138 | "torv3" -> 3 139 | "torv2" -> 2 140 | else -> if (configs.isTorv3) 3 else null 141 | } 142 | optionsManager.proxyEnabled = configs.isProxyEnabled 143 | optionsManager.proxyUrl = "%s:%d".format(proxyIp, proxyPort) 144 | optionsManager.torVersion = torVersion 145 | log(PluginLog.INFO, "btcli4j: proxy enable at ${optionsManager.proxyUrl}") 146 | log(PluginLog.INFO, "btcli4j: proxy type is ${configs.proxy.type}") 147 | log(PluginLog.INFO, "btcli4j: tor version is $torVersion") 148 | } 149 | 150 | if (prunedMode && bitcoinRpcPass.trim().isNotEmpty() && bitcoinRpcUser.trim().isNotEmpty()) { 151 | PluginManager.instance.bitcoinPass = bitcoinRpcPass 152 | PluginManager.instance.bitcoinUser = bitcoinRpcUser 153 | PluginManager.instance.baseBitcoinUrl = bitcoinBaseUrl 154 | PluginManager.instance.prunedMode = true 155 | } 156 | HttpRequestFactory.initHttpClient(optionsManager) 157 | pluginInit = true 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/MediationMethod.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control 20 | 21 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.EstimateFeeCommand 22 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetChainInfoCommand 23 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetRawBlockByHeightCommand 24 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetUtxOutCommand 25 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 26 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.SendRawTransactionCommand 27 | import jrpc.clightning.plugins.CLightningPlugin 28 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 29 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 30 | import io.vincenzopalazzo.btcli4j.control.commands.btcprune.EstimateFeeBtc 31 | import io.vincenzopalazzo.btcli4j.control.commands.btcprune.GetChainInfoBtc 32 | import io.vincenzopalazzo.btcli4j.control.commands.btcprune.GetRawBlockByHeightBtc 33 | import io.vincenzopalazzo.btcli4j.control.commands.btcprune.GetUtxOutBtc 34 | import io.vincenzopalazzo.btcli4j.control.commands.btcprune.SendRawTransactionBtc 35 | import io.vincenzopalazzo.btcli4j.util.PluginManager 36 | 37 | public enum class Command(private val commandName: String) { 38 | GetChainInfoBtc("getchaininfo"), 39 | EstimateFeeBtc("estimatefees"), 40 | GetRawBlockByHeightBtc("getrawblockbyheight"), 41 | GetUtxOutBtc("getutxout"), 42 | SendRawTransactionBtc("sendrawtransaction"); 43 | 44 | override fun toString(): String { 45 | return commandName 46 | } 47 | } 48 | 49 | /** 50 | * @author https://github.com/vincenzopalazzo 51 | */ 52 | object MediationMethod { 53 | 54 | private val commands: HashMap = HashMap() 55 | 56 | init { 57 | // The prune mode need to be overridden with a enum 58 | // because it is possible use the bitcoin RPC interface 59 | // to talk with the bitcoin node and also with all the othe rimplementation 60 | // that use the Bitcoin RPC 1.0 interface (e.g Litecoin) 61 | if (PluginManager.instance.prunedMode) { 62 | // adding prune command 63 | val bitcoinRPC = LiteBitcoinRPC( 64 | PluginManager.instance.bitcoinUser, 65 | PluginManager.instance.bitcoinPass, 66 | PluginManager.instance.baseBitcoinUrl 67 | ) 68 | 69 | commands.apply { 70 | put(Command.GetChainInfoBtc.toString(), GetChainInfoBtc(bitcoinRPC)) 71 | put(Command.EstimateFeeBtc.toString(), EstimateFeeBtc(bitcoinRPC)) 72 | put(Command.GetRawBlockByHeightBtc.toString(), GetRawBlockByHeightBtc(bitcoinRPC)) 73 | put(Command.GetUtxOutBtc.toString(), GetUtxOutBtc(bitcoinRPC)) 74 | put(Command.SendRawTransactionBtc.toString(), SendRawTransactionBtc(bitcoinRPC)) 75 | } 76 | } else { 77 | commands.apply { 78 | put(Command.GetChainInfoBtc.toString(), GetChainInfoCommand()) 79 | put(Command.EstimateFeeBtc.toString(), EstimateFeeCommand()) 80 | put(Command.GetRawBlockByHeightBtc.toString(), GetRawBlockByHeightCommand()) 81 | put(Command.GetUtxOutBtc.toString(), GetUtxOutCommand()) 82 | put(Command.SendRawTransactionBtc.toString(), SendRawTransactionCommand()) 83 | } 84 | } 85 | } 86 | 87 | fun runCommand(key: String, plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 88 | if (commands.containsKey(key)) { 89 | commands.getValue(key).run(plugin, request, response) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/ChainOfResponsibilityCheck.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain 2 | 3 | import io.vincenzopalazzo.btcli4j.control.Command 4 | import io.vincenzopalazzo.btcli4j.control.checkchain.checks.BlockNotFoundCheck 5 | import io.vincenzopalazzo.btcli4j.control.checkchain.checks.CheckHandler 6 | import io.vincenzopalazzo.btcli4j.control.checkchain.checks.GetUtxoInvalidFormat 7 | import io.vincenzopalazzo.btcli4j.control.checkchain.checks.TransactionCheck 8 | import jrpc.clightning.plugins.CLightningPlugin 9 | import okio.ByteString 10 | 11 | /** 12 | * Chain of responsibility that manage all the type of error that can occurs during 13 | * the plugin workflow, mostly due to the Rest API fault. 14 | * 15 | * FIXME: Some checks are done inside HttpRequestFactory because it is easily make re retry logic but 16 | * this is a terrible performance leak. 17 | * 18 | * Some other check like checkByCommand are done inside the command logics, and this usually help to avoid 19 | * hack in the source code like response == "Block not found". 20 | * 21 | * @author https://github.com/vincenzopalazzo 22 | */ 23 | class ChainOfResponsibilityCheck { 24 | 25 | private val checksHandles: ArrayList = ArrayList() 26 | private var commandsCheck: HashMap> = HashMap() 27 | 28 | init { 29 | initChains() 30 | } 31 | 32 | private fun initChains() { 33 | checksHandles.addAll( 34 | listOf( 35 | BlockNotFoundCheck(), 36 | TransactionCheck() 37 | ) 38 | ) 39 | commandsCheck[Command.GetUtxOutBtc.toString()] = listOf(GetUtxoInvalidFormat()) 40 | } 41 | 42 | fun check(plugin: CLightningPlugin, response: ByteString): CheckResult { 43 | for (check in checksHandles) { 44 | val result = check.check(plugin, response) 45 | if (!result.next) 46 | return result 47 | } 48 | // this should be never happen 49 | return CheckResult(false, ByteString.of("Check fails".toByte())) 50 | } 51 | 52 | fun checkCommand(forCommand: Command, plugin: CLightningPlugin, response: ByteString): CheckResult { 53 | if (commandsCheck.containsKey(forCommand.toString())) { 54 | val handler = commandsCheck[forCommand.toString()]!! 55 | handler.forEach { 56 | val result = it.check(plugin, response) 57 | if (!result.next) 58 | return result 59 | } 60 | return CheckResult(false, response) 61 | } 62 | throw IllegalArgumentException("No sanity check for command $forCommand") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/CheckResult.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain 2 | 3 | import okio.ByteString 4 | 5 | /** 6 | * This simple class manage check inside the chain, in particular each check return a CheckResult 7 | * and the chain manage this object to make the correct behaviors 8 | * FIXME: add a static string for the failure, maybe we can remove the null 9 | */ 10 | class CheckResult(val next: Boolean, val result: ByteString? = null) { 11 | 12 | /** 13 | * Return true if the request contains a valid format, otherwise 14 | * false. 15 | */ 16 | fun isSafe(): Boolean { 17 | return result != null && !result.utf8().contains("Check fails") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/checks/BlockNotFoundCheck.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain.checks 2 | 3 | import io.vincenzopalazzo.btcli4j.control.checkchain.CheckResult 4 | import jrpc.clightning.plugins.CLightningPlugin 5 | import okio.ByteString 6 | 7 | /** 8 | * When the c-lightning will ask the next block and this block 9 | * it is not available, the request will be not available and for this 10 | * reason we need to make a check of the message inside this request. 11 | */ 12 | class BlockNotFoundCheck : CheckHandler { 13 | override fun check(plugin: CLightningPlugin, response: ByteString): CheckResult { 14 | if (response.utf8().contentEquals("Block not found")) { 15 | return CheckResult(false, response) 16 | } 17 | return CheckResult(true) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/checks/CheckHandler.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain.checks 2 | 3 | import io.vincenzopalazzo.btcli4j.control.checkchain.CheckResult 4 | import jrpc.clightning.plugins.CLightningPlugin 5 | import okio.ByteString 6 | 7 | interface CheckHandler { 8 | fun check(plugin: CLightningPlugin, response: ByteString): CheckResult 9 | } 10 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/checks/GetUtxoInvalidFormat.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain.checks 2 | 3 | import com.google.gson.JsonSyntaxException 4 | import io.vincenzopalazzo.btcli4j.control.checkchain.CheckResult 5 | import io.vincenzopalazzo.btcli4j.model.esplora.BTCTransactionModel 6 | import io.vincenzopalazzo.btcli4j.util.JSONConverter 7 | import jrpc.clightning.plugins.CLightningPlugin 8 | import jrpc.clightning.plugins.log.PluginLog 9 | import okio.ByteString 10 | 11 | class GetUtxoInvalidFormat : CheckHandler { 12 | override fun check(plugin: CLightningPlugin, response: ByteString): CheckResult { 13 | // Issue: https://github.com/clightning4j/btcli4j/issues/59 14 | // this is a monkey check 15 | try { 16 | if (response.utf8().isNotEmpty() && !JSONConverter.isJSONNull(response.utf8())) { 17 | // FIXME: The check result can be generic, and we can return the type of the decoding 18 | JSONConverter.deserialize( 19 | response.utf8(), 20 | BTCTransactionModel::class.java 21 | ) 22 | return CheckResult(next = true, response) 23 | } 24 | } catch (jsonException: JsonSyntaxException) { 25 | plugin.log(PluginLog.ERROR, "We receive a 200 code from the server, but the reponse looks invalid") 26 | plugin.log(PluginLog.ERROR, jsonException.message) 27 | } 28 | return CheckResult(next = false) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/checkchain/checks/TransactionCheck.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.control.checkchain.checks 2 | 3 | import io.vincenzopalazzo.btcli4j.control.checkchain.CheckResult 4 | import jrpc.clightning.plugins.CLightningPlugin 5 | import okio.ByteString 6 | 7 | class TransactionCheck : CheckHandler { 8 | override fun check(plugin: CLightningPlugin, response: ByteString): CheckResult { 9 | val responseString = response.utf8() 10 | // What this error means from esplora API? 11 | if (responseString.contains("bad-txns-inputs-missingorspent", true)) { 12 | return CheckResult(false, ByteString.EMPTY) 13 | } else if (responseString.contains("Transaction already in block chain", true)) { 14 | return CheckResult(false, ByteString.EMPTY) 15 | } 16 | return CheckResult(true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/ICommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands 20 | 21 | import jrpc.clightning.plugins.CLightningPlugin 22 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 23 | 24 | /** 25 | * @author https://github.com/vincenzopalazzo 26 | */ 27 | interface ICommand { 28 | 29 | fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/btcprune/EstimateFeeBtc.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.btcprune 20 | 21 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 22 | import io.github.clightning4j.litebtc.exceptions.BitcoinCoreException 23 | import io.github.clightning4j.litebtc.exceptions.LiteBitcoinRPCException 24 | import io.github.clightning4j.litebtc.model.generic.Parameters 25 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 26 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.EstimateFeeCommand 27 | import io.vincenzopalazzo.btcli4j.model.bitcoin.EstimateFeeBitcoin 28 | import io.vincenzopalazzo.btcli4j.util.PluginManager 29 | import jrpc.clightning.plugins.CLightningPlugin 30 | import jrpc.clightning.plugins.log.PluginLog 31 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 32 | 33 | class EstimateFeeParams(val lnmode: String, val target: Int, val mode: String) 34 | 35 | class FeeManager { 36 | private val feesEstimation: HashMap = HashMap() 37 | private val params = listOf( 38 | EstimateFeeParams("FEERATE_HIGHEST", 2, "CONSERVATIVE"), 39 | EstimateFeeParams("FEERATE_URGENT", 6, "ECONOMICAL"), 40 | EstimateFeeParams("FEERATE_NORMAL", 12, "ECONOMICAL"), 41 | EstimateFeeParams("FEERATE_SLOW", 100, "ECONOMICAL") 42 | ) 43 | 44 | private fun estimateFee(rpc: LiteBitcoinRPC, param: EstimateFeeParams): EstimateFeeBitcoin { 45 | // TODO: try to use this transaction getmempoolinfo 46 | // read this issue https://github.com/ElementsProject/lightning/issues/4473#issue-853325816 47 | val params = Parameters("estimatesmartfee") 48 | params.addParameter("conf_target", param.target) 49 | params.addParameter("estimate_mode", param.mode) 50 | return rpc.makeBitcoinRequest(params, EstimateFeeBitcoin::class.java) 51 | } 52 | 53 | fun estimate(rpc: LiteBitcoinRPC, plugin: CLightningPlugin): Boolean { 54 | params.forEach { 55 | val estimateFee = this.estimateFee(rpc, it) 56 | if (estimateFee.errors?.isNotEmpty() == true) { 57 | plugin.log(PluginLog.ERROR, "Error during the fee estimation") 58 | for (error in estimateFee.errors!!) { 59 | plugin.log(PluginLog.ERROR, "EstimateFeeBtc: %s".format(error)) 60 | } 61 | return false 62 | } 63 | estimateFee.convertBtcToSat() 64 | plugin.log( 65 | PluginLog.DEBUG, 66 | "EstimateFeeBtc: Estimate fee (target: %d mode: %s) calculation from bitcoin core: %d".format( 67 | it.target, 68 | it.target, 69 | estimateFee.feeRate!!.toInt() 70 | ) 71 | ) 72 | feesEstimation[it.lnmode] = estimateFee 73 | } 74 | return true 75 | } 76 | 77 | fun urgentFee(): EstimateFeeBitcoin { 78 | return feesEstimation["FEERATE_URGENT"]!! 79 | } 80 | 81 | fun highestFee(): EstimateFeeBitcoin { 82 | return feesEstimation["FEERATE_HIGHEST"]!! 83 | } 84 | 85 | fun normalFee(): EstimateFeeBitcoin { 86 | return feesEstimation["FEERATE_NORMAL"]!! 87 | } 88 | 89 | fun slowFee(): EstimateFeeBitcoin { 90 | return feesEstimation["FEERATE_SLOW"]!! 91 | } 92 | } 93 | 94 | /** 95 | * @author https://github.com/vincenzopalazzo 96 | */ 97 | class EstimateFeeBtc( 98 | private val bitcoinRPC: LiteBitcoinRPC, 99 | private val alternative: EstimateFeeCommand = EstimateFeeCommand() 100 | ) : ICommand { 101 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 102 | if (PluginManager.instance.isDownloading) { 103 | plugin.log(PluginLog.DEBUG, "EstimateFeeBtc: Share message to esplora") 104 | alternative.run(plugin, request, response) 105 | return 106 | } 107 | try { 108 | val estimator = FeeManager() 109 | if (!estimator.estimate(bitcoinRPC, plugin)) { 110 | // FIXME this can cause a loop? maybe 111 | this.returnNullFee(response) 112 | return 113 | } 114 | 115 | response.apply { 116 | add("opening", estimator.normalFee().feeRate!!.toInt()) 117 | add("mutual_close", estimator.slowFee().feeRate!!.toInt()) 118 | add("unilateral_close", estimator.urgentFee().feeRate!!.toInt()) 119 | add("delayed_to_us", estimator.normalFee().feeRate!!.toInt()) 120 | add("htlc_resolution", estimator.urgentFee().feeRate!!.toInt()) 121 | add("penalty", estimator.normalFee().feeRate!!.toInt()) 122 | add("min_acceptable", estimator.slowFee().feeRate!!.toInt() / 2) 123 | add("max_acceptable", estimator.highestFee().feeRate!!.toInt() * 10) 124 | } 125 | } catch (bitcoinEx: BitcoinCoreException) { 126 | plugin.log(PluginLog.ERROR, "EstimateFeeBtc: terminate bitcoin core with error: %s".format(bitcoinEx.message)) 127 | this.returnNullFee(response) 128 | } catch (ex: LiteBitcoinRPCException) { 129 | plugin.log(PluginLog.ERROR, ex.stackTraceToString()) 130 | plugin.log(PluginLog.DEBUG, "EstimateFeeBtc: Share message to esplora") 131 | alternative.run(plugin, request, response) 132 | } 133 | } 134 | 135 | private fun returnNullFee(response: CLightningJsonObject) { 136 | response.apply { 137 | add("opening", null) 138 | add("mutual_close", null) 139 | add("unilateral_close", null) 140 | add("delayed_to_us", null) 141 | add("htlc_resolution", null) 142 | add("penalty", null) 143 | add("min_acceptable", null) 144 | add("max_acceptable", null) 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/btcprune/GetChainInfoBtc.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.btcprune 20 | 21 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 22 | import io.github.clightning4j.litebtc.exceptions.BitcoinCoreException 23 | import io.github.clightning4j.litebtc.exceptions.LiteBitcoinRPCException 24 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 25 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetChainInfoCommand 26 | import io.vincenzopalazzo.btcli4j.model.bitcoin.BlockchainInfoBitcoin 27 | import io.vincenzopalazzo.btcli4j.util.PluginManager 28 | import jrpc.clightning.plugins.CLightningPlugin 29 | import jrpc.clightning.plugins.exceptions.CLightningPluginException 30 | import jrpc.clightning.plugins.log.PluginLog 31 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 32 | import java.lang.Exception 33 | import kotlin.concurrent.thread 34 | 35 | /** 36 | * @author https://github.com/vincenzopalazzo 37 | */ 38 | class GetChainInfoBtc( 39 | private val bitcoinRPC: LiteBitcoinRPC, 40 | private val alternative: GetChainInfoCommand = GetChainInfoCommand() 41 | ) : ICommand { 42 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 43 | try { 44 | var network = plugin.configs.network 45 | network = convertNameToBitcoinCore(network) 46 | val blockchainInfo = bitcoinRPC.makeBitcoinRequest("getblockchaininfo", BlockchainInfoBitcoin::class.java) 47 | plugin.log(PluginLog.ERROR, blockchainInfo.chain!!) 48 | if (blockchainInfo.chain!! != network) { 49 | throw CLightningPluginException(400, "Bitcoin Core and C-lightning are running over different chain.") 50 | } 51 | 52 | if (blockchainInfo.isDownloading!!) { 53 | PluginManager.instance.isDownloading = blockchainInfo.isDownloading!! 54 | thread(isDaemon = true, start = true) { 55 | checkBitcoinStatus(bitcoinRPC, plugin) 56 | } 57 | plugin.log(PluginLog.DEBUG, "GetChainInfoBtc: Share message to esplora") 58 | alternative.run(plugin, request, response) 59 | return 60 | } 61 | response.apply { 62 | add("chain", blockchainInfo.chain!!) 63 | add("headercount", blockchainInfo.headerCount!!) 64 | add("blockcount", blockchainInfo.blockCount!!) 65 | add("ibd", blockchainInfo.isDownloading!!) 66 | } 67 | } catch (exception: CLightningPluginException) { 68 | plugin.log(PluginLog.ERROR, "GetChainInfoBtc: Wrong chain") 69 | throw exception 70 | } catch (exception: Exception) { 71 | when (exception) { 72 | is BitcoinCoreException -> { 73 | plugin.log(PluginLog.ERROR, "GetChainInfoBtc: terminate bitcoin core with error: %s".format(exception.message)) 74 | } 75 | is LiteBitcoinRPCException -> { 76 | plugin.log(PluginLog.ERROR, exception.stackTraceToString()) 77 | } 78 | } 79 | plugin.log(PluginLog.DEBUG, "GetChainInfoBtc: Share message to esplora") 80 | alternative.run(plugin, request, response) 81 | } 82 | } 83 | 84 | private fun convertNameToBitcoinCore(chainName: String): String { 85 | when (chainName) { 86 | "bitcoin" -> return "main" 87 | "testnet" -> return "test" 88 | "regtest" -> return "regtest" 89 | } 90 | throw CLightningPluginException(400, "Unknown name chain %s under Bitcoin Core".format(chainName)) 91 | } 92 | 93 | private fun checkBitcoinStatus(bitcoinRPC: LiteBitcoinRPC, plugin: CLightningPlugin, timeout: Long = 60000) { 94 | while (PluginManager.instance.isDownloading) { 95 | try { 96 | val info = bitcoinRPC.makeBitcoinRequest("getblockchaininfo", BlockchainInfoBitcoin::class.java) 97 | plugin.log(PluginLog.DEBUG, "GetChainInfoBtc: Status bitcoin core in the check thread: %s".format(info.isDownloading!!.toString())) 98 | PluginManager.instance.isDownloading = info.isDownloading!! 99 | } catch (exception: Exception) { 100 | when (exception) { 101 | is BitcoinCoreException -> { 102 | plugin.log( 103 | PluginLog.ERROR, 104 | "GetChainInfoBtc: terminate bitcoin core in the check thread with error: %s".format(exception.message) 105 | ) 106 | } 107 | is LiteBitcoinRPCException -> { 108 | plugin.log(PluginLog.ERROR, "GetChainInfoBtc: Exception in the check thread\n" + exception.stackTraceToString()) 109 | } 110 | } 111 | } finally { 112 | Thread.sleep(timeout) 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/btcprune/GetRawBlockByHeightBtc.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.btcprune 20 | 21 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 22 | import io.github.clightning4j.litebtc.exceptions.BitcoinCoreException 23 | import io.github.clightning4j.litebtc.exceptions.LiteBitcoinRPCException 24 | import io.github.clightning4j.litebtc.model.generic.Parameters 25 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 26 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetRawBlockByHeightCommand 27 | import io.vincenzopalazzo.btcli4j.util.PluginManager 28 | import jrpc.clightning.plugins.CLightningPlugin 29 | import jrpc.clightning.plugins.log.PluginLog 30 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 31 | 32 | /** 33 | * @author https://github.com/vincenzopalazzo 34 | */ 35 | class GetRawBlockByHeightBtc( 36 | private val bitcoinRPC: LiteBitcoinRPC, 37 | private val alternative: GetRawBlockByHeightCommand = GetRawBlockByHeightCommand() 38 | ) : ICommand { 39 | 40 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 41 | if (PluginManager.instance.isDownloading) { 42 | plugin.log(PluginLog.DEBUG, "GetRawBlockByHeightBtc: Share message to esplora") 43 | alternative.run(plugin, request, response) 44 | return 45 | } 46 | try { 47 | val heightRequested = request["height"].asLong 48 | var params = Parameters("getblockhash") 49 | params.addParameter("height", heightRequested) 50 | val hashBlock = bitcoinRPC.makeBitcoinRequest(params, String::class.java) 51 | if (hashBlock == null || hashBlock.isEmpty()) { 52 | plugin.log(PluginLog.ERROR, "GetRawBlockByHeightBtc: Bad getblockhash result %s".format(hashBlock)) 53 | alternative.run(plugin, request, response) 54 | return 55 | } 56 | params = Parameters("getblock") 57 | params.addParameter("blockhash", hashBlock) 58 | params.addParameter("verbose", 0) // return only the string 59 | val hexBlock = bitcoinRPC.makeBitcoinRequest(params, String::class.java) 60 | if (hexBlock == null || hexBlock.isEmpty()) { 61 | plugin.log(PluginLog.ERROR, "GetRawBlockByHeightBtc: Bad blockhash result %s".format(hashBlock)) 62 | alternative.run(plugin, request, response) 63 | return 64 | } 65 | response.apply { 66 | add("blockhash", hashBlock) 67 | add("block", hexBlock) 68 | } 69 | } catch (bitcoinEx: BitcoinCoreException) { 70 | // TODO: this should be not throws, because Bitcoin core return an exception with all the propriety null, also the error. 71 | plugin.log(PluginLog.DEBUG, "GetRawBlockByHeightBtc: terminate bitcoin core with error: %s".format(bitcoinEx.message)) 72 | response.apply { 73 | add("blockhash", null) 74 | add("block", null) 75 | } 76 | } catch (exception: LiteBitcoinRPCException) { 77 | plugin.log(PluginLog.ERROR, exception.stackTraceToString()) 78 | plugin.log(PluginLog.DEBUG, "GetRawBlockByHeightBtc: Share message to esplora") 79 | alternative.run(plugin, request, response) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/btcprune/GetUtxOutBtc.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.btcprune 20 | 21 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 22 | import io.github.clightning4j.litebtc.exceptions.BitcoinCoreException 23 | import io.github.clightning4j.litebtc.exceptions.LiteBitcoinRPCException 24 | import io.github.clightning4j.litebtc.model.generic.Parameters 25 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 26 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.GetUtxOutCommand 27 | import io.vincenzopalazzo.btcli4j.model.bitcoin.UTXOBitcoin 28 | import io.vincenzopalazzo.btcli4j.util.JSONConverter 29 | import io.vincenzopalazzo.btcli4j.util.PluginManager 30 | import jrpc.clightning.plugins.CLightningPlugin 31 | import jrpc.clightning.plugins.log.PluginLog 32 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 33 | 34 | /** 35 | * @author https://github.com/vincenzopalazzo 36 | */ 37 | class GetUtxOutBtc(private val bitcoinRPC: LiteBitcoinRPC, private val alternative: GetUtxOutCommand = GetUtxOutCommand()) : ICommand { 38 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 39 | if (PluginManager.instance.isDownloading) { 40 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Share message to esplora") 41 | alternative.run(plugin, request, response) 42 | return 43 | } 44 | try { 45 | val txId = request["txid"].asString 46 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: TxId -> $txId") 47 | val vOut = request["vout"].asInt 48 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Vout -> $vOut") 49 | val params = Parameters("gettxout") 50 | params.addParameter("txid", txId) 51 | params.addParameter("n", vOut) 52 | // params.addParameter("include_mempool", true) TODO: double check 53 | val getUtxo: UTXOBitcoin? = bitcoinRPC.makeBitcoinRequest(params, UTXOBitcoin::class.java) 54 | plugin.log( 55 | PluginLog.DEBUG, 56 | "GetUtxOutBtc: Bitcoin getutxo response is %s".format( 57 | JSONConverter.serialize( 58 | getUtxo ?: "Is null" 59 | ) 60 | ) 61 | ) 62 | // TODO: bitcoin core should return an error? 63 | if (getUtxo?.amount == null || getUtxo.script == null || getUtxo.script.hex == null || getUtxo.script.hex!!.isEmpty()) { 64 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Received undefined response, return a null response") 65 | response.apply { 66 | add("amount", null) 67 | add("script", null) 68 | } 69 | return 70 | } 71 | getUtxo.convertBtcToSat() 72 | // Check if the data are valid, otherwise put the message to esplora 73 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: answer from gettxout: " + JSONConverter.serialize(getUtxo)) 74 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Amount tx (not conv to integer): " + getUtxo.amount!!) 75 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Script hex: %s".format(getUtxo.script.hex!!)) 76 | response.apply { 77 | add("amount", getUtxo.amount!!.toInt()) 78 | add("script", getUtxo.script.hex!!) 79 | } 80 | } catch (bitcoinEx: BitcoinCoreException) { 81 | plugin.log(PluginLog.ERROR, "GetUtxOutBtc: terminate bitcoin core with error: %s".format(bitcoinEx.message)) 82 | response.apply { 83 | add("amount", null) 84 | add("script", null) 85 | } 86 | } catch (exception: LiteBitcoinRPCException) { 87 | plugin.log(PluginLog.ERROR, exception.stackTraceToString()) 88 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: Share message to esplora") 89 | alternative.run(plugin, request, response) 90 | } 91 | plugin.log(PluginLog.DEBUG, "GetUtxOutBtc: answer is: " + JSONConverter.serialize(response)) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/btcprune/SendRawTransactionBtc.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020-2021 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.btcprune 20 | 21 | import io.github.clightning4j.litebtc.LiteBitcoinRPC 22 | import io.github.clightning4j.litebtc.exceptions.BitcoinCoreException 23 | import io.github.clightning4j.litebtc.exceptions.LiteBitcoinRPCException 24 | import io.github.clightning4j.litebtc.model.generic.Parameters 25 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 26 | import io.vincenzopalazzo.btcli4j.control.commands.esplora.SendRawTransactionCommand 27 | import io.vincenzopalazzo.btcli4j.util.PluginManager 28 | import jrpc.clightning.plugins.CLightningPlugin 29 | import jrpc.clightning.plugins.log.PluginLog 30 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 31 | import java.lang.Exception 32 | 33 | /** 34 | * @author https://github.com/vincenzopalazzo 35 | */ 36 | class SendRawTransactionBtc(private val bitcoinRPC: LiteBitcoinRPC, private val alternative: SendRawTransactionCommand = SendRawTransactionCommand()) : ICommand { 37 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 38 | if (PluginManager.instance.isDownloading) { 39 | plugin.log(PluginLog.DEBUG, "SendRawTransactionBtc: Share message to esplora") 40 | alternative.run(plugin, request, response) 41 | return 42 | } 43 | try { 44 | // TODO support allowhighfees 45 | val txRaw = request["tx"].asString 46 | val params = Parameters("sendrawtransaction") 47 | params.addParameter("hexstring", txRaw) 48 | val transactionId = bitcoinRPC.makeBitcoinRequest(params, String::class.java) 49 | if (transactionId == null || transactionId.isEmpty()) { 50 | plugin.log(PluginLog.DEBUG, "SendRawTransactionBtc: The transaction id has a bad format %s".format(transactionId)) 51 | plugin.log(PluginLog.DEBUG, "SendRawTransactionBtc: Share message to esplora") 52 | alternative.run(plugin, request, response) 53 | return 54 | } 55 | response.apply { 56 | add("success", transactionId.isNotEmpty()) // TODO validate if it is a txId 57 | // add("errmsg", transactionId.isNotEmpty()) // in case of error I will share the content to esplora. 58 | } 59 | } catch (exception: Exception) { 60 | when (exception) { 61 | is BitcoinCoreException -> { 62 | plugin.log(PluginLog.ERROR, "GetChainInfoBtc: terminate bitcoin core with error: %s".format(exception.message)) 63 | response.apply { 64 | add("success", false) 65 | add("errmsg", exception.message) 66 | } 67 | } 68 | is LiteBitcoinRPCException -> { 69 | plugin.log(PluginLog.ERROR, exception.stackTraceToString()) 70 | plugin.log(PluginLog.DEBUG, "SendRawTransactionBtc: Share message to esplora") 71 | alternative.run(plugin, request, response) 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/esplora/EstimateFeeCommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.esplora 20 | 21 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 22 | import io.vincenzopalazzo.btcli4j.model.esplora.EstimateFeeModel 23 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 24 | import io.vincenzopalazzo.btcli4j.util.JSONConverter 25 | import jrpc.clightning.plugins.CLightningPlugin 26 | import jrpc.clightning.plugins.exceptions.CLightningPluginException 27 | import jrpc.clightning.plugins.log.PluginLog 28 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 29 | 30 | /** 31 | * @author https://github.com/vincenzopalazzo 32 | */ 33 | class EstimateFeeCommand : ICommand { 34 | 35 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 36 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 37 | 38 | try { 39 | val reqEstimateFee = HttpRequestFactory.createRequest("%s/fee-estimates".format(queryUrl))!! 40 | val estimateFee: EstimateFeeModel 41 | 42 | val resEstimateFee = HttpRequestFactory.execRequest(plugin, reqEstimateFee).utf8() 43 | if (resEstimateFee.isNotEmpty() && !reqEstimateFee.equals("{}")) { 44 | plugin.log(PluginLog.DEBUG, "EstimateFeeCommand: Estimate fee $resEstimateFee") 45 | estimateFee = JSONConverter.deserialize(resEstimateFee, EstimateFeeModel::class.java) 46 | } else { 47 | plugin.log(PluginLog.ERROR, "EstimateFeeCommand: Estimate fee empty response without object !!!") 48 | throw CLightningPluginException(400, "EstimateFeeCommand: Estimate fee empty from http response") 49 | } 50 | 51 | if (!estimateFee.isEmpty()) { 52 | response.apply { 53 | add("opening", estimateFee.estimateFeeForNormalTarget().toInt()) 54 | add("mutual_close", estimateFee.estimateFeeRate().toInt()) 55 | add("unilateral_close", estimateFee.estimateFeeForUrgentTarget().toInt()) 56 | add("delayed_to_us", estimateFee.estimateFeeRate().toInt()) 57 | add("htlc_resolution", estimateFee.estimateFeeForNormalTarget().toInt()) 58 | add("penalty", estimateFee.getAverageEstimateFee().toInt()) 59 | add("min_acceptable", estimateFee.estimateFeeRate().toInt() / 2) 60 | add("max_acceptable", estimateFee.estimateFeeForVeryUrgentTarget().toInt() * 10) 61 | } 62 | } else if (estimateFee.isEmpty()) { 63 | plugin.log(PluginLog.DEBUG, "Estimate fee empty") 64 | response.apply { 65 | add("opening", null) 66 | add("mutual_close", null) 67 | add("unilateral_close", null) 68 | add("delayed_to_us", null) 69 | add("htlc_resolution", null) 70 | add("penalty", null) 71 | add("min_acceptable", null) 72 | add("max_acceptable", null) 73 | } 74 | } 75 | } catch (ex: Exception) { 76 | plugin.log(PluginLog.ERROR, ex.localizedMessage) 77 | throw CLightningPluginException(400, ex.localizedMessage) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/esplora/GetChainInfoCommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.esplora 20 | 21 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 22 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 23 | import jrpc.clightning.plugins.CLightningPlugin 24 | import jrpc.clightning.plugins.exceptions.CLightningPluginException 25 | import jrpc.clightning.plugins.log.PluginLog 26 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 27 | import okhttp3.MediaType.Companion.toMediaType 28 | 29 | /** 30 | * @author https://github.com/vincenzopalazzo 31 | */ 32 | class GetChainInfoCommand : ICommand { 33 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 34 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 35 | try { 36 | val reqGenesisBlock = HttpRequestFactory.createRequest( 37 | "%s/block-height/0".format(queryUrl), 38 | mediaType = "text/plain".toMediaType() 39 | ) 40 | plugin.log(PluginLog.DEBUG, reqGenesisBlock!!.url.toUrl()) 41 | val genesisBlock: String = HttpRequestFactory.execRequest(plugin, reqGenesisBlock).utf8() 42 | plugin.log(PluginLog.DEBUG, "GetChainInfoCommand: Genesis block %s".format(genesisBlock)) 43 | 44 | val reqBlockchainHeight = HttpRequestFactory.createRequest( 45 | "%s/blocks/tip/height".format(queryUrl), 46 | mediaType = "text/plain".toMediaType() 47 | ) 48 | val blockCount: Int 49 | if (reqBlockchainHeight != null) { 50 | blockCount = HttpRequestFactory.execRequest(plugin, reqBlockchainHeight).utf8().toInt() 51 | plugin.log(PluginLog.DEBUG, "GetChainInfoCommand: GetChainInfoCommand: Block count = %s".format(blockCount.toString())) 52 | } else { 53 | plugin.log(PluginLog.ERROR, "GetChainInfoCommand: Request for genesis block null!!!") 54 | throw CLightningPluginException(400, "Request for genesis block null!!!") 55 | } 56 | 57 | var chain = "" // TODO refactoring this inside a new lib tools :-) 58 | when (genesisBlock) { 59 | "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" -> chain = "main" 60 | "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" -> chain = "test" 61 | "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" -> chain = "regtest" 62 | "1466275836220db2944ca059a3a10ef6fd2ea684b0688d2c379296888a206003" -> chain = "liquidv1" 63 | } 64 | 65 | response.apply { 66 | add("chain", chain) 67 | add("headercount", blockCount) 68 | add("blockcount", blockCount) 69 | add("ibd", false) 70 | } 71 | } catch (ex: Exception) { 72 | plugin.log(PluginLog.WARNING, "GetChainInfoCommand terminate with an exception\n %s".format(ex.stackTraceToString())) 73 | throw CLightningPluginException(400, ex.localizedMessage) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/esplora/GetRawBlockByHeightCommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.esplora 20 | 21 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 22 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 23 | import jrpc.clightning.plugins.CLightningPlugin 24 | import jrpc.clightning.plugins.log.PluginLog 25 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 26 | import okhttp3.MediaType.Companion.toMediaType 27 | 28 | /** 29 | * @author https://github.com/vincenzopalazzo 30 | */ 31 | class GetRawBlockByHeightCommand : ICommand { 32 | 33 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 34 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 35 | val heightRequest = request["height"].asLong 36 | try { 37 | val blockWithHeight = HttpRequestFactory.createRequest( 38 | "%s/block-height/%s".format(queryUrl, heightRequest), 39 | mediaType = "text/plain".toMediaType() 40 | )!! 41 | // get block reference by height 42 | val resBlockHash = HttpRequestFactory.execRequest(plugin, blockWithHeight).utf8() 43 | plugin.log(PluginLog.DEBUG, "GetRawBlockByHeightCommand: $blockWithHeight Hash $resBlockHash") 44 | val hexBlock: String 45 | if (resBlockHash.isNotEmpty() && resBlockHash != "Block not found") { 46 | // get the raw block by block hash 47 | val blockWithHash = HttpRequestFactory.createRequest( 48 | "%s/block/%s/raw".format(queryUrl, resBlockHash), 49 | mediaType = "text/plain".toMediaType() 50 | )!! 51 | blockWithHash.header("Content-Encoding: gzip") 52 | hexBlock = HttpRequestFactory.execRequest(plugin, blockWithHash).hex() 53 | if (hexBlock.length < 150) { 54 | // 150 is a random number there is any real motivation. 55 | // sanity check, esplora return a wrong value as block hex; 56 | // the problem is also described here 57 | returnResponse(response) 58 | } else { 59 | returnResponse(response, resBlockHash, hexBlock) 60 | } 61 | } else { 62 | // Lightningd continue to require bitcoin block and it know that the block is the last 63 | // only if it receive the object with null proprieties 64 | returnResponse(response) 65 | } 66 | } catch (ex: Exception) { 67 | plugin.log(PluginLog.WARNING, "GetRawBlockByHeightCommand has some exception\n".format(ex.stackTraceToString())) 68 | // throw CLightningPluginException(ex.cause) 69 | returnResponse(response) 70 | } 71 | } 72 | 73 | private fun returnResponse(response: CLightningJsonObject, blockHash: String? = null, blockHex: String? = null) { 74 | response.apply { 75 | add("blockhash", blockHash) 76 | add("block", blockHex) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/esplora/GetUtxOutCommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.esplora 20 | 21 | import io.vincenzopalazzo.btcli4j.control.Command 22 | import io.vincenzopalazzo.btcli4j.control.checkchain.ChainOfResponsibilityCheck 23 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 24 | import io.vincenzopalazzo.btcli4j.model.esplora.BTCTransactionModel 25 | import io.vincenzopalazzo.btcli4j.model.esplora.StatusUTXOModel 26 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 27 | import io.vincenzopalazzo.btcli4j.util.JSONConverter 28 | import jrpc.clightning.plugins.CLightningPlugin 29 | import jrpc.clightning.plugins.exceptions.CLightningPluginException 30 | import jrpc.clightning.plugins.log.PluginLog 31 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 32 | import java.lang.reflect.InvocationTargetException 33 | 34 | /** 35 | * @author https://github.com/vincenzopalazzo 36 | */ 37 | class GetUtxOutCommand(private val sanityCheck: ChainOfResponsibilityCheck = ChainOfResponsibilityCheck()) : ICommand { 38 | 39 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 40 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 41 | val txId = request["txid"].asString 42 | plugin.log(PluginLog.DEBUG, "GetUtxOutCommand: TxId: $txId") 43 | val vOut = request["vout"].asInt 44 | plugin.log(PluginLog.DEBUG, "GetUtxOutCommand: Vout: $vOut") 45 | try { 46 | if (getUTXOInformation(plugin, txId, vOut, response)) { 47 | // The transaction wasn't spent!! 48 | val reqTxInformation = HttpRequestFactory.createRequest("%s/tx/%s".format(queryUrl, txId))!! 49 | val resTxInformation = HttpRequestFactory.execRequest(plugin, reqTxInformation) 50 | // the API can return an impartial answer, and this required a monkey sanity check! 51 | val checkResult = sanityCheck.checkCommand(Command.GetUtxOutBtc, plugin, resTxInformation) 52 | if (checkResult.isSafe()) { 53 | val cleanResponse = checkResult.result!!.utf8() 54 | val transactionInformation = JSONConverter.deserialize( 55 | cleanResponse, 56 | BTCTransactionModel::class.java 57 | ) 58 | val transactionOutput = transactionInformation.transactionsOutput?.get(vOut)!! 59 | response.apply { 60 | add("amount", transactionOutput.value) 61 | add("script", transactionOutput.scriptPubKey!!) 62 | } 63 | return 64 | } 65 | response.apply { 66 | add("amount", null) 67 | add("script", null) 68 | } 69 | } 70 | } catch (invocation: InvocationTargetException) { 71 | // Caused by: https://github.com/clightning4j/btcli4j/pull/60#issuecomment-1174881779 72 | // Source: https://stackoverflow.com/a/6020758/10854225 73 | val ex = invocation.cause!! 74 | plugin.log(PluginLog.WARNING, ex.localizedMessage) 75 | ex.printStackTrace() 76 | throw CLightningPluginException(400, ex.localizedMessage) 77 | } catch (ex: Exception) { 78 | plugin.log(PluginLog.WARNING, ex.localizedMessage) 79 | ex.printStackTrace() 80 | throw CLightningPluginException(400, ex.localizedMessage) 81 | } 82 | } 83 | 84 | /** 85 | * Function to verify if transaction have a status valid, if it is spent I will return false and the transaction will be not verify. 86 | * On the other hand, if it is not spent I return true to continue and get the transaction information! 87 | */ 88 | private fun getUTXOInformation( 89 | plugin: CLightningPlugin, 90 | txId: String, 91 | vout: Int, 92 | response: CLightningJsonObject 93 | ): Boolean { 94 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 95 | val requestUtxo = HttpRequestFactory.createRequest("%s/tx/%s/outspend/%s".format(queryUrl, txId, vout))!! 96 | val resUtxo = HttpRequestFactory.execRequest(plugin, requestUtxo).utf8() 97 | if (resUtxo.isNotEmpty() /*&& resUtxo !== "{}"*/) { 98 | val statusUtxo = JSONConverter.deserialize(resUtxo, StatusUTXOModel::class.java) 99 | /* As of at least v0.15.1.0, bitcoind returns "success" but an empty 100 | string on a spent txout. */ 101 | plugin.log(PluginLog.DEBUG, "GetUtxOutCommand: UTXO Status: %s".format(JSONConverter.serialize(statusUtxo))) 102 | if (statusUtxo.spend) { 103 | plugin.log(PluginLog.DEBUG, "GetUtxOutCommand: Tx with id: $txId was spent") 104 | response.apply { 105 | add("amount", null) 106 | add("script", null) 107 | } 108 | return false 109 | } 110 | return true // continue 111 | } 112 | response.apply { 113 | add("amount", null) 114 | add("script", null) 115 | } 116 | return false 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/control/commands/esplora/SendRawTransactionCommand.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.control.commands.esplora 20 | 21 | import io.vincenzopalazzo.btcli4j.control.commands.ICommand 22 | import io.vincenzopalazzo.btcli4j.util.HttpRequestFactory 23 | import jrpc.clightning.plugins.CLightningPlugin 24 | import jrpc.clightning.plugins.log.PluginLog 25 | import jrpc.service.converters.jsonwrapper.CLightningJsonObject 26 | import okhttp3.MediaType.Companion.toMediaType 27 | 28 | /** 29 | * @author https://github.com/vincenzopalazzo 30 | */ 31 | class SendRawTransactionCommand : ICommand { 32 | override fun run(plugin: CLightningPlugin, request: CLightningJsonObject, response: CLightningJsonObject) { 33 | val queryUrl = HttpRequestFactory.buildQueryRL(plugin.configs.network) 34 | 35 | val txRaw = request["tx"].asString 36 | try { 37 | val reqSendTx = HttpRequestFactory.createRequest( 38 | "%s/tx".format(queryUrl), type = "post", body = txRaw, 39 | mediaType = "plain/text".toMediaType() 40 | )!! 41 | 42 | val resSendTx = HttpRequestFactory.execRequest(plugin, reqSendTx).utf8() 43 | response.apply { 44 | add("success", resSendTx.isNotEmpty()) // TODO validate if it is a txId 45 | add("errmsg", resSendTx.isNotEmpty()) // check this code 46 | } 47 | } catch (ex: Exception) { 48 | plugin.log(PluginLog.WARNING, ex.localizedMessage) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/bitcoin/BlockchainInfoBitcoin.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.model.bitcoin 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | class BlockchainInfoBitcoin { 6 | var chain: String? = null 7 | @SerializedName("initialblockdownload") 8 | var isDownloading: Boolean? = null 9 | @SerializedName("headers") 10 | var headerCount: Long? = null 11 | @SerializedName("blocks") 12 | var blockCount: Long? = null 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/bitcoin/EstimateFeeBitcoin.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.model.bitcoin 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | class EstimateFeeBitcoin { 6 | @SerializedName("feerate") 7 | var feeRate: Double? = null 8 | var errors: ArrayList? = null 9 | var blocks: Long? = null 10 | 11 | fun convertBtcToSat() { 12 | this.feeRate = this.feeRate!! * 10_000_000 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/bitcoin/MemPoolInfo.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.model.bitcoin 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.math.BigInteger 5 | 6 | class MemPoolInfo { 7 | @SerializedName("loaded") 8 | var isReady: Boolean? = null 9 | var size: BigInteger? = null 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/bitcoin/UTXOBitcoin.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.model.bitcoin 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | class UTXOBitcoin { 6 | @SerializedName("value") 7 | var amount: Double? = null 8 | @SerializedName("scriptPubKey") 9 | val script: ScriptPubKey? = null 10 | 11 | fun convertBtcToSat() { 12 | this.amount = this.amount!! * 10_000_000 13 | } 14 | } 15 | 16 | class ScriptPubKey { 17 | var hex: String? = null 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/BTCTransactionInputModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | import com.google.gson.JsonObject 22 | import com.google.gson.annotations.SerializedName 23 | 24 | /** 25 | * @author https://github.com/vincenzopalazzo 26 | */ 27 | class BTCTransactionInputModel { 28 | 29 | @SerializedName("txid") 30 | val txId: String? = null 31 | 32 | val vout: Long = 0 33 | 34 | @SerializedName("prevout") 35 | val preVout: JsonObject? = null // What is the return type here? 36 | 37 | @SerializedName("scriptsig") 38 | val scriptSig: String? = null 39 | 40 | @SerializedName("scriptsig_asm") 41 | val scriptsigAsm: String? = null 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/BTCTransactionModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | import com.google.gson.annotations.SerializedName 22 | 23 | /** 24 | * @author https://github.com/vincenzopalazzo 25 | */ 26 | class BTCTransactionModel { 27 | 28 | @SerializedName("txid") 29 | val txId: String? = null 30 | 31 | val version: Int = 0 32 | 33 | @SerializedName("locktime") 34 | val lockTime: Long = 0 35 | 36 | @SerializedName("vin") 37 | val transactionsInputs: List? = null 38 | 39 | @SerializedName("vout") 40 | val transactionsOutput: List? = null 41 | 42 | val size: Long = 0 43 | 44 | val weight: Long = 0 45 | 46 | val fee: Long = 0 47 | 48 | val status: TransactionStatusModel? = null 49 | } 50 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/BTCTransactionOutputModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | import com.google.gson.annotations.SerializedName 22 | 23 | /** 24 | * @author https://github.com/vincenzopalazzo 25 | */ 26 | class BTCTransactionOutputModel { 27 | 28 | @SerializedName("scriptpubkey") 29 | val scriptPubKey: String? = null 30 | 31 | @SerializedName("scriptpubkey_asm") 32 | val scriptPubKeyAsm: String? = null 33 | 34 | @SerializedName("scriptpubkey_type") 35 | val scriptPubKeyType: String? = null 36 | 37 | val value: Long = 0 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/EstimateFeeModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | /** 22 | * @author https://github.com/vincenzopalazzo 23 | */ 24 | class EstimateFeeModel { 25 | 26 | val mapEstimationFee = HashMap() 27 | 28 | fun putValue(key: Int, value: Double) { 29 | mapEstimationFee.put(key, value) 30 | } 31 | 32 | fun getValue(key: Int): Double { 33 | return mapEstimationFee.getOrDefault(key, 0.0) 34 | } 35 | 36 | fun containsKey(key: Int): Boolean { 37 | return mapEstimationFee.containsKey(key) 38 | } 39 | 40 | fun isEmpty(): Boolean { 41 | return mapEstimationFee.isEmpty() 42 | } 43 | 44 | /** 45 | * This method calculate the take the latest feerate return from the API in the latest block 46 | * with the esplora api the minimum blocks are 6, if this value is not present 47 | * it is calculate the minimum number of blocks in the JSON response. 48 | */ 49 | fun estimateFeeRate(): Double { 50 | var minKey = 0 51 | var result = 0.0 52 | if (mapEstimationFee.containsKey(6)) { 53 | return mapEstimationFee[6]!! 54 | } 55 | 56 | mapEstimationFee.forEach { 57 | if (minKey > it.key) { 58 | minKey = it.key 59 | result = it.value 60 | } 61 | } 62 | return result 63 | } 64 | 65 | fun estimateFeeForNormalTarget(): Double { 66 | if (containsKey(5)) { 67 | return getValue(5) 68 | } else if (containsKey(6)) { 69 | return getValue(6) 70 | } else if (containsKey(4)) { 71 | return getValue(5) 72 | } 73 | return estimateFeeRate() 74 | } 75 | 76 | fun estimateFeeForUrgentTarget(): Double { 77 | if (containsKey(3)) { 78 | return getValue(3) 79 | } else if (containsKey(2)) { 80 | return getValue(2) 81 | } else if (containsKey(4)) { 82 | return getValue(4) 83 | } 84 | // TOD change this with very urgent 85 | return estimateFeeForVeryUrgentTarget() 86 | } 87 | 88 | fun estimateFeeForVeryUrgentTarget(): Double { 89 | var bigKey = 0 90 | var result = 0.0 91 | if (mapEstimationFee.containsKey(2)) { 92 | return mapEstimationFee[2]!! 93 | } 94 | mapEstimationFee.forEach { 95 | if (bigKey > it.key) { 96 | bigKey = it.key 97 | result = it.value 98 | } 99 | } 100 | return result 101 | } 102 | 103 | // FIXME very stupid fee estimation! 104 | fun getAverageEstimateFee(): Double { 105 | var correctValue = 0.0 106 | mapEstimationFee.forEach { 107 | correctValue += it.value 108 | } 109 | return correctValue / mapEstimationFee.size 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/StatusOnChain.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.model.esplora 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | class StatusOnChain { 6 | 7 | var confirmed: Boolean? = null 8 | 9 | @SerializedName("block_height") 10 | var blockHeight: Long? = null 11 | 12 | @SerializedName("block_hash") 13 | var blockHash: String? = null 14 | 15 | @SerializedName("block_time") 16 | var blockTime: Long? = null 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/StatusUTXOModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | /** 22 | * @author https://github.com/vincenzopalazzo 23 | */ 24 | class StatusUTXOModel { 25 | 26 | var spend: Boolean = false 27 | 28 | var txid: String? = null 29 | 30 | var vin: Int? = null 31 | 32 | var status: StatusOnChain? = null 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/model/esplora/TransactionStatusModel.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.model.esplora 20 | 21 | import com.google.gson.annotations.SerializedName 22 | 23 | /** 24 | * @author https://github.com/vincenzopalazzo 25 | */ 26 | class TransactionStatusModel { 27 | 28 | val confirmed: Boolean = false 29 | 30 | @SerializedName("block_height") 31 | val blockHeight: Long = 0 32 | 33 | @SerializedName("block_hash") 34 | val blockHash: String? = null 35 | 36 | @SerializedName("block_time") 37 | val blockTime: Long = 0 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/util/HttpRequestFactory.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.util 20 | 21 | import io.vincenzopalazzo.btcli4j.control.checkchain.ChainOfResponsibilityCheck 22 | import java.net.InetSocketAddress 23 | import java.net.Proxy 24 | import java.util.concurrent.TimeUnit 25 | import jrpc.clightning.plugins.CLightningPlugin 26 | import jrpc.clightning.plugins.log.PluginLog 27 | import okhttp3.MediaType 28 | import okhttp3.MediaType.Companion.toMediaType 29 | import okhttp3.OkHttpClient 30 | import okhttp3.Request 31 | import okhttp3.RequestBody.Companion.toRequestBody 32 | import okhttp3.Response 33 | import okio.ByteString 34 | 35 | /** 36 | * @author https://github.com/vincenzopalazzo 37 | */ 38 | object HttpRequestFactory { 39 | 40 | private lateinit var optionsManager: OptionsManager 41 | private var retryTime = 4 42 | private var checkChains = ChainOfResponsibilityCheck() 43 | private lateinit var baseUrl: String 44 | private var client = OkHttpClient.Builder() 45 | .retryOnConnectionFailure(true) 46 | .connectTimeout(1, TimeUnit.MINUTES) 47 | .writeTimeout(1, TimeUnit.MINUTES) 48 | .readTimeout(1, TimeUnit.MINUTES) 49 | .build() 50 | 51 | fun initHttpClient(optionsManager: OptionsManager) { 52 | this.optionsManager = optionsManager 53 | if (optionsManager.proxyEnabled) { 54 | this.configureProxy(optionsManager.proxyUrl, optionsManager.torVersion != null) 55 | } 56 | baseUrl = optionsManager.getEndPointUrl() 57 | } 58 | 59 | private fun configureProxy(proxyString: String, tor: Boolean = true) { 60 | val tokens = proxyString.split(":") 61 | val ip = tokens[0] 62 | val port = tokens[1] 63 | val proxyAddr = InetSocketAddress(ip, port.toInt()) 64 | if (tor) { 65 | val proxyTor = Proxy(Proxy.Type.SOCKS, proxyAddr) 66 | client = OkHttpClient.Builder() 67 | .retryOnConnectionFailure(true) 68 | .proxy(proxyTor) 69 | .connectTimeout(2, TimeUnit.MINUTES) 70 | .writeTimeout(2, TimeUnit.MINUTES) 71 | .readTimeout(2, TimeUnit.MINUTES) 72 | .build() 73 | } 74 | } 75 | 76 | fun buildQueryRL(network: String): String { 77 | if (network == "bitcoin") { 78 | return "api" 79 | } 80 | return "$network/api" 81 | } 82 | 83 | fun createRequest( 84 | url: String, 85 | type: String = "get", 86 | body: String = "", 87 | mediaType: MediaType = "application/json; charset=utf-8".toMediaType() 88 | ): Request? { 89 | val completeUrl = "%s/%s".format(baseUrl, url) 90 | when (type) { 91 | "get" -> return buildGetRequest(completeUrl) 92 | "post" -> return buildPostRequest(completeUrl, body, mediaType) 93 | } 94 | return null 95 | } 96 | 97 | /** 98 | * This method is designed to retry the request 4 time and wait an exponential time 99 | * this, the wait time is set to 1 minutes by default and the wait time is exponential, 100 | * So this mean that the wait time is set to 101 | */ 102 | @Throws(Exception::class) 103 | fun execRequest(plugin: CLightningPlugin, request: Request): ByteString { 104 | for (time in 0..retryTime) { 105 | try { 106 | val response = makeRequest(request) 107 | if (isValid(response)) { 108 | val result = response.body!!.byteString() 109 | plugin.log(PluginLog.DEBUG, "Request to ${request.url} with the following result $result") 110 | return result 111 | } 112 | // there are some cases where the invalid answer can be accepted from lightnind 113 | val result = response.body!!.byteString() 114 | val checkResult = checkChains.check(plugin, result) 115 | // This is a error with the a 200 code 116 | // this can happen often with the client-server communication 117 | if (checkResult.result!!.utf8() == "Check fails") { 118 | plugin.log(PluginLog.DEBUG, "An wrong status happen in retry time $time") 119 | plugin.log(PluginLog.WARNING, "Response invalid, all the plugin check on the request failed") 120 | waitingToRetry(plugin, retryTime) 121 | continue 122 | } 123 | return result 124 | } catch (ex: Exception) { 125 | plugin.log(PluginLog.DEBUG, "An wrong status happen in retry time $time") 126 | plugin.log(PluginLog.ERROR, "Error during the request method %s".format(ex.localizedMessage)) 127 | waitingToRetry(plugin, retryTime) 128 | } 129 | } 130 | // never found a valid answer and I'm exit with an error. 131 | plugin.log(PluginLog.ERROR, "After all retry time ($retryTime) all request failed") 132 | throw Exception("Error generate from Bitcoin backend more than 4 time") 133 | } 134 | 135 | private fun waitingToRetry(plugin: CLightningPlugin, retryTime: Int) { 136 | val exponentialRetryTime = optionsManager.waitingTime * (retryTime + 1) 137 | plugin.log( 138 | PluginLog.WARNING, 139 | "Error occurs %d time: and the waiting time is set to %d".format(retryTime, exponentialRetryTime) 140 | ) 141 | Thread.sleep(exponentialRetryTime.toLong()) 142 | } 143 | 144 | private fun makeRequest(request: Request): Response { 145 | return client.newCall(request).execute() 146 | } 147 | 148 | private fun isValid(response: Response?): Boolean { 149 | return response != null && response.isSuccessful 150 | } 151 | 152 | private fun buildPostRequest(url: String, body: String, mediaType: MediaType): Request { 153 | val requestBody = body.toRequestBody(mediaType) 154 | return Request.Builder() 155 | .url(url) 156 | .post(requestBody) 157 | .build() 158 | } 159 | 160 | private fun buildGetRequest(url: String): Request { 161 | return Request.Builder() 162 | .url(url) 163 | .build() 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/util/JSONConverter.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * C-lightning plugin to override Bitcoin backend plugin. 3 | * Copyright (C) 2020 Vincenzo Palazzo vincenzopalazzodev@gmail.com 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | package io.vincenzopalazzo.btcli4j.util 20 | 21 | import com.google.gson.GsonBuilder 22 | import com.google.gson.JsonPrimitive 23 | import io.vincenzopalazzo.btcli4j.model.esplora.EstimateFeeModel 24 | import io.vincenzopalazzo.btcli4j.util.typeadapter.EstimateFeeTypeAdapter 25 | import java.lang.reflect.Type 26 | 27 | /** 28 | * @author https://github.com/vincenzopalazzo 29 | */ 30 | object JSONConverter { 31 | 32 | private val gsonBuilder = GsonBuilder() 33 | 34 | init { 35 | gsonBuilder.registerTypeAdapter(EstimateFeeModel::class.java, EstimateFeeTypeAdapter()) 36 | gsonBuilder.setPrettyPrinting() 37 | } 38 | 39 | private val gson = gsonBuilder 40 | .serializeNulls() 41 | .create() 42 | 43 | fun serialize(obj: Any): String? { 44 | return gson.toJson(obj) 45 | } 46 | 47 | fun deserialize(fromString: String, responseType: Type): T { 48 | try { 49 | return gson.fromJson(fromString, responseType) 50 | } catch (ex: Exception) { 51 | ex.printStackTrace() 52 | throw RuntimeException(ex.cause) 53 | } 54 | } 55 | 56 | fun isJSONNull(fromString: String): Boolean { 57 | val type = gson.fromJson(fromString, JsonPrimitive::class.java) 58 | return type.isJsonNull 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/util/OptionsManager.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.util 2 | 3 | class OptionsManager(private val url: String, val waitingTime: Int, var torVersion: Int?, var proxyEnabled: Boolean, var proxyUrl: String) { 4 | companion object { 5 | // Default values 6 | private const val BASE_URL = "https://blockstream.info" 7 | private const val BASE_URL_TORV3 = "http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion" 8 | private const val BASE_URL_TORV2 = "http://explorernuoc63nb.onion" // TODO: This is deprecated in the lightningd from august 2021 9 | } 10 | 11 | private fun customEndPoint(): Boolean { 12 | return when (url) { 13 | BASE_URL -> false 14 | BASE_URL_TORV2 -> false 15 | BASE_URL_TORV3 -> false 16 | else -> true 17 | } 18 | } 19 | 20 | fun getEndPointUrl(): String { 21 | if (customEndPoint()) 22 | return url 23 | return when (proxyEnabled) { 24 | true -> if (torVersion == 3) BASE_URL_TORV3 else if (torVersion == 2) BASE_URL_TORV2 else BASE_URL 25 | false -> BASE_URL 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/util/PluginManager.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.util 2 | 3 | class PluginManager { 4 | 5 | companion object { 6 | val instance = PluginManager() 7 | } 8 | 9 | var prunedMode: Boolean = false 10 | var bitcoinPass = "" 11 | var bitcoinUser = "" 12 | var baseBitcoinUrl = "" 13 | var isDownloading = false 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/io/vincenzopalazzo/btcli4j/util/typeadapter/EstimateFeeTypeAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.vincenzopalazzo.btcli4j.util.typeadapter 2 | 3 | import com.google.gson.TypeAdapter 4 | import com.google.gson.stream.JsonReader 5 | import com.google.gson.stream.JsonToken 6 | import com.google.gson.stream.JsonWriter 7 | import io.vincenzopalazzo.btcli4j.model.esplora.EstimateFeeModel 8 | import java.math.BigDecimal 9 | 10 | class EstimateFeeTypeAdapter : TypeAdapter() { 11 | 12 | override fun write(jsonWriter: JsonWriter, obj: EstimateFeeModel) { 13 | jsonWriter.beginObject() 14 | 15 | obj.mapEstimationFee.forEach { 16 | jsonWriter.name(it.key.toString()) 17 | jsonWriter.value(BigDecimal.valueOf(it.value)) 18 | } 19 | jsonWriter.endObject() 20 | } 21 | 22 | override fun read(jsonReader: JsonReader): EstimateFeeModel { 23 | val feerate = EstimateFeeModel() 24 | jsonReader.beginObject() 25 | while (jsonReader.hasNext()) { 26 | var token = jsonReader.peek() 27 | val key: Int 28 | if (token == JsonToken.NAME) { 29 | key = jsonReader.nextName().toInt() 30 | // move to next token 31 | token = jsonReader.peek() 32 | feerate.putValue(key, BigDecimal.valueOf(jsonReader.nextDouble() * 100).toDouble()) 33 | } 34 | } 35 | jsonReader.endObject() 36 | return feerate 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ${logging.appender.console.level:-OFF} 25 | 26 | 27 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 28 | 29 | 30 | 31 | 32 | ${user.home}/clightning4j/btcli4j.log 33 | false 34 | 35 | true 36 | 38 | 39 | %-4relative [%thread] %-5level %logger{35} - %msg%n 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------