├── .classpath ├── .gitignore ├── .project ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── docs ├── blockexplorer.md ├── exchangerates.md ├── pushtx.md ├── receive.md ├── statistics.md └── wallet.md ├── pom.xml └── src ├── main └── java │ └── info │ └── blockchain │ └── api │ ├── APIException.java │ ├── HttpClient.java │ ├── HttpClientInterface.java │ ├── OkClient.java │ ├── Util.java │ ├── blockexplorer │ ├── BlockExplorer.java │ └── entity │ │ ├── Address.java │ │ ├── AddressSummary.java │ │ ├── Balance.java │ │ ├── Block.java │ │ ├── FilterType.java │ │ ├── Input.java │ │ ├── LatestBlock.java │ │ ├── MultiAddress.java │ │ ├── MultiAddressBalance.java │ │ ├── Output.java │ │ ├── SimpleBlock.java │ │ ├── Transaction.java │ │ ├── UnspentOutput.java │ │ └── XpubFull.java │ ├── etc │ └── Base58.java │ ├── exchangerates │ ├── Currency.java │ └── ExchangeRates.java │ ├── pushtx │ └── PushTx.java │ ├── receive │ ├── CallbackLog.java │ ├── Receive.java │ └── ReceiveResponse.java │ ├── statistics │ ├── Chart.java │ ├── Point.java │ ├── Statistics.java │ └── StatisticsResponse.java │ └── wallet │ ├── Wallet.java │ └── entity │ ├── Address.java │ ├── CreateWalletResponse.java │ └── PaymentResponse.java └── test └── java └── info └── blockchain └── api ├── AppTest.java └── blockexplorer └── BlockExplorerTest.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | # Maven 15 | target/ 16 | pom.xml.tag 17 | pom.xml.releaseBackup 18 | pom.xml.versionsBackup 19 | pom.xml.next 20 | release.properties 21 | dependency-reduced-pom.xml 22 | buildNumber.properties 23 | .mvn/timing.properties 24 | 25 | # Eclipse 26 | .metadata 27 | bin/ 28 | tmp/ 29 | *.tmp 30 | *.bak 31 | *.swp 32 | *~.nib 33 | local.properties 34 | .settings/ 35 | .loadpath 36 | .recommenders 37 | 38 | # Eclipse Core 39 | .project 40 | 41 | # External tool builders 42 | .externalToolBuilders/ 43 | 44 | # Locally stored "Eclipse launch configurations" 45 | *.launch 46 | 47 | # PyDev specific (Python IDE for Eclipse) 48 | *.pydevproject 49 | 50 | # CDT-specific (C/C++ Development Tooling) 51 | .cproject 52 | 53 | # JDT-specific (Eclipse Java Development Tools) 54 | .classpath 55 | 56 | # Java annotation processor (APT) 57 | .factorypath 58 | 59 | # PDT-specific (PHP Development Tools) 60 | .buildpath 61 | 62 | # sbteclipse plugin 63 | .target 64 | 65 | # Tern plugin 66 | .tern-project 67 | 68 | # TeXlipse plugin 69 | .texlipse 70 | 71 | # STS (Spring Tool Suite) 72 | .springBeans 73 | 74 | # Code Recommenders 75 | .recommenders/ 76 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | api 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [2.0.0] - 2017-06-16 8 | ### Added 9 | - This CHANGELOG file to record all the changes in current and following version of this project. 10 | - Filter, limit, and offset optional parameters to getAddress in BlockExplorer. 11 | - Confirms and limit optional parameters to getUnspentOutputs in BlockExplorer. 12 | - A new getBalance method in BlockExplorer. 13 | - A new getMultiAddress method in BlockExplorer. 14 | - A new getXpub method in BlockExplorer. 15 | - A new toFiat method in ExchangeRates. 16 | - A new checkGap method in Receive. 17 | - A new getCallbackLog method in Receive. 18 | - A new getChart method in Statistics. 19 | - A new getPools method in Statistics. 20 | 21 | ### Changed 22 | - ExchangeRate series interfaces to allow API be used for all methods. 23 | - Receive series interfaces to allow API be used for all methods. 24 | - Statistics series interfaces to allow API be used for all methods. 25 | - Moved createWallet functions from CreateWallet class to Wallet class. 26 | 27 | ### Deprecated 28 | - “Get transaction and block by index” interfaces in BlockExplorer. 29 | 30 | ### Removed 31 | - Method getInventoryData in BlockExplorer 32 | - Parameter “note” from send and sendMany methods in Wallet. 33 | - Parameter “confirmations” from listAddress and getAddress method in Wallet. 34 | - Method consolidate in Wallet. 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Blockchain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain API library (Java, v2.0.0) 2 | 3 | An official Java library for interacting with the Blockchain.info API (Java 1.6 required). 4 | 5 | ### Getting started 6 | 7 | If you prefer building from source: 8 | 9 | ``` 10 | $ git clone https://github.com/blockchain/api-v1-client-java 11 | $ cd api-v1-client-java 12 | $ mvn install 13 | ``` 14 | 15 | We also provide a snapshot Maven repository for users who prefer managing dependencies that way. 16 | 17 | Add the following to your pom.xml: 18 | ```xml 19 | 20 | ... 21 | 22 | ... 23 | 24 | api-v1-client-java-mvn-repo 25 | https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo/ 26 | 27 | true 28 | always 29 | 30 | 31 | 32 | ... 33 | 34 | ... 35 | 36 | info.blockchain 37 | api 38 | LATEST 39 | 40 | 41 | ... 42 | 43 | ``` 44 | 45 | The above Maven repository also works with Gradle. 46 | 47 | Add this to your `build.gradle`: 48 | ``` 49 | ... 50 | repositories { 51 | ... 52 | maven { 53 | url = 'https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo' 54 | } 55 | } 56 | ... 57 | dependencies { 58 | ... 59 | compile 'info.blockchain:api:2.0.2' 60 | } 61 | ... 62 | ``` 63 | 64 | Note that the above procedures require Maven or Gradle. If you do not wish to use Maven or Gradle, please compile the source manually. 65 | 66 | The library consists of the following packages: 67 | 68 | * `info.blockchain.api.blockexplorer` ([docs](docs/blockexplorer.md)) ([api/blockchain_api][api1]) 69 | * `info.blockchain.api.createwallet` ([docs](docs/createwallet.md)) ([api/create_wallet][api2]) 70 | * `info.blockchain.api.exchangerates` ([docs](docs/exchangerates.md)) ([api/exchange\_rates\_api][api3]) 71 | * `info.blockchain.api.pushtx` ([docs](docs/pushtx.md)) ([pushtx][api7]) 72 | * `info.blockchain.api.receive` ([docs](docs/receive.md)) ([api/api_receive][api4]) 73 | * `info.blockchain.api.statistics` ([docs](docs/statistics.md)) ([api/charts_api][api5]) 74 | * `info.blockchain.api.wallet` ([docs](docs/wallet.md)) ([api/blockchain\_wallet\_api][api6]) 75 | 76 | In order to use `createwallet` and `wallet` you need to run an instance of [service-my-wallet-v3](https://github.com/blockchain/service-my-wallet-v3). 77 | 78 | ### Error handling 79 | 80 | All methods may throw exceptions caused by incorrectly passed parameters or other problems. If a call is rejected server-side, the `APIException` exception will be thrown. In case of a network error, the `IOException` exception will be thrown. 81 | 82 | ### Connection timeouts 83 | 84 | It is possible to set arbitrary connection timeouts. 85 | 86 | ```java 87 | info.blockchain.api.HttpClient.TIMEOUT_MS = 2000; // time out after 2000 milliseconds 88 | ``` 89 | 90 | ### Request limits and API keys 91 | 92 | In order to prevent abuse some API methods require an API key approved with some basic contact information and a description of its intended use. Please request an API key [here](https://blockchain.info/api/api_create_code). 93 | 94 | The same API key can be used to bypass the request limiter. 95 | 96 | ### Code Coverage Report generation 97 | 98 | To generate the code coverage report, execute the following command: 99 | ```sh 100 | mvn clean verify 101 | ``` 102 | 103 | This will generate a code coverage report in `target/site/jacoco/index.html`. 104 | 105 | 106 | [api1]: https://blockchain.info/api/blockchain_api 107 | [api2]: https://blockchain.info/api/create_wallet 108 | [api3]: https://blockchain.info/api/exchange_rates_api 109 | [api4]: https://blockchain.info/api/api_receive 110 | [api5]: https://blockchain.info/api/charts_api 111 | [api6]: https://blockchain.info/api/blockchain_wallet_api 112 | [api7]: https://blockchain.info/pushtx 113 | -------------------------------------------------------------------------------- /docs/blockexplorer.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.blockexplorer` package 2 | 3 | The `blockexplorer` package contains the `BlockExplorer` class that reflects the functionality documented at https://blockchain.info/api/blockchain_api. It can be used to query the block chain, fetch block, transaction and address data, get unspent outputs for an address etc. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.blockexplorer.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | // instantiate a block explorer 17 | BlockExplorer blockExplorer = new BlockExplorer(); 18 | 19 | // get a transaction by hash and list the value of all its inputs 20 | Transaction tx = blockExplorer.getTransaction("df67414652722d38b43dcbcac6927c97626a65bd4e76a2e2787e22948a7c5c47"); 21 | for (Input i : tx.getInputs()) 22 | { 23 | System.out.println(i.getPreviousOutput().getValue()); 24 | } 25 | 26 | // get a block by hash and read the number of transactions in the block 27 | Block block = blockExplorer.getBlock("0000000000000000050fe18c9b961fc7c275f02630309226b15625276c714bf1"); 28 | int numberOfTxsInBlock = block.getTransactions().size(); 29 | 30 | // get an address and read its final balance 31 | Address address = blockExplorer.getAddress("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"); 32 | long finalBalance = address.getFinalBalance(); 33 | 34 | // get an address and read its final balance with filter, limit, and offset 35 | Address address = client.getAddress("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", FilterType.All, 10, 5); 36 | long finalBalance = address.getFinalBalance(); 37 | 38 | 39 | // get a list of currently unconfirmed transactions and print the relay IP address for each 40 | List unconfirmedTxs = blockExplorer.getUnconfirmedTransactions(); 41 | for (Transaction unconfTx : unconfirmedTxs) 42 | System.out.println(tx.getRelayedBy()); 43 | 44 | // calculate the balanace of an address by fetching a list of all its unspent outputs 45 | List outs = blockExplorer.getUnspentOutputs("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"); 46 | long totalUnspentValue = 0; 47 | for (UnspentOutput out : outs) 48 | totalUnspentValue += out.getValue(); 49 | 50 | // calculate the balanace of an address by fetching a list of all its unspent outputs with confirmations and limit 51 | List outs = blockExplorer.getUnspentOutputs(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), 5, 10); 52 | long totalUnspentValue = 0; 53 | for (UnspentOutput out : outs) 54 | totalUnspentValue += out.getValue(); 55 | 56 | // returns the address balance summary for each address provided 57 | Map balances = blockExplorer.getBalance(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), FilterType.All); 58 | 59 | // returns an aggregated summary on all addresses provided. 60 | MultiAddress multiAddr = blockExplorer.getMultiAddress(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), FilterType.All, 10, 5); 61 | 62 | // returns xpub summary on a xpub provided, with its overall balance and its transactions. 63 | XpubFull xpub = blockExplorer.getXpub("xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn", FilterType.All, 10, 5); 64 | 65 | // get the latest block on the main chain and read its height 66 | LatestBlock latestBlock = blockExplorer.getLatestBlock(); 67 | long latestBlockHeight = latestBlock.getHeight(); 68 | 69 | // use the previous block height to get a list of blocks at that height 70 | // and detect a potential chain fork 71 | List blocksAtHeight = blockExplorer.getBlocksAtHeight(latestBlockHeight); 72 | if (blocksAtHeight.size() > 1) 73 | System.out.println("The chain has forked!"); 74 | else 75 | System.out.println("The chain is still in one piece :)"); 76 | 77 | // get a list of all blocks that were mined today since 00:00 UTC 78 | List todaysBlocks = blockExplorer.getBlocks(); 79 | System.out.println(todaysBlocks.size()); 80 | } 81 | } 82 | 83 | ``` -------------------------------------------------------------------------------- /docs/exchangerates.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.exchangerates` package 2 | 3 | The `exchangerates` package contains the `ExchangeRates` class that reflects the functionality documented at https://blockchain.info/api/exchange_rates_api. It allows users to get price tickers for most major currencies and directly convert fiat amounts to BTC. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.exchangerates.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | // get the Exchange object 17 | ExchangeRates exchange = new ExchangeRates(); 18 | 19 | // get the ticker map 20 | Map ticker = exchange.getTicker(); 21 | BigDecimal BTCUSDsell = ticker.get("USD").getSell(); 22 | 23 | // convert 5362 EUR to BTC 24 | BigDecimal EURToBTC = exchange.toBTC("EUR", new BigDecimal(53620)); 25 | 26 | // convert 100,000,000 satoshi to USD 27 | BigDecimal BTCToUSD = exchange.toFiat("USD", new BigDecimal(100000000)); 28 | } 29 | } 30 | 31 | ``` -------------------------------------------------------------------------------- /docs/pushtx.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.pushtx` package 2 | 3 | The `pushtx` package contains the `PushTx` class that reflects the functionality provided at https://blockchain.info/pushtx . It allows users to broadcast hex encoded transactions to the bitcoin network. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.pushtx.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | PushTx.pushTx( "0100000001fd468e431cf5797b108e4d22724e1e055b3ecec59af4ef17b063afd36d3c5cf6010000008c4930460221009918eee8be186035be8ca573b7a4ef7bc672c59430785e5390cc375329a2099702210085b86387e3e15d68c847a1bdf786ed0fdbc87ab3b7c224f3c5490ac19ff4e756014104fe2cfcf0733e559cbf28d7b1489a673c0d7d6de8470d7ff3b272e7221afb051b777b5f879dd6a8908f459f950650319f0e83a5cf1d7c1dfadf6458f09a84ba80ffffffff01185d2033000000001976a9144be9a6a5f6fb75765145d9c54f1a4929e407d2ec88ac00000000"); 17 | } 18 | } 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/receive.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.receive` package 2 | 3 | The `receive` package contains the `Receive` class that reflects the functionality documented at https://blockchain.info/api/api_receive. It allows merchants to derive addresses from their HD wallets and be notified upon payment. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.receive.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | Receive receive = new Receive("YOUR_API_CODE"); 17 | 18 | ReceiveResponse response = receive.receive( 19 | "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", 20 | URLEncoder.encode("https://your.url.com?secret=foo", "UTF-8")); 21 | 22 | System.out.println(String.format("The receiving address is %s. " 23 | + "The address index is %d", 24 | response.getReceivingAddress(), 25 | response.getIndex())); 26 | 27 | int xpubGap = receive.checkGap("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"); 28 | CallbackLog cbLog = receive.getCallbackLog(URLEncoder.encode("https://your.url.com?secret=foo", "UTF-8")); 29 | } 30 | } 31 | 32 | ``` -------------------------------------------------------------------------------- /docs/statistics.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.statistics` package 2 | 3 | The `statistics` package contains the `Statistics` class that reflects the functionality documented at at https://blockchain.info/api/charts_api. It makes various network statistics available, such as the total number of blocks in existence, next difficulty retarget block, total BTC mined in the past 24 hours etc. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.statistics.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | Statistics stats = new Statistics(); 17 | 18 | StatisticsResponse stats = stats.get(); 19 | System.out.println(String.format("The current difficulty is %s. " 20 | + "The next retarget will happen in %s hours.", 21 | stats.getDifficulty(), 22 | (stats.getNextRetarget() - stats.getTotalBlocks()) * stats.getMinutesBetweenBlocks() / 60)); 23 | 24 | Chart txPerSec = stats.getChart("transactions-per-second", "5weeks", "8hours"); 25 | 26 | Map pools = stats.getPools("5weeks"); 27 | } 28 | } 29 | 30 | ``` -------------------------------------------------------------------------------- /docs/wallet.md: -------------------------------------------------------------------------------- 1 | ## `info.blockchain.api.wallet` package 2 | 3 | The `wallet` package contains the `Wallet` class that reflects the functionality documented at at https://blockchain.info/api/blockchain_wallet_api. It allows users to directly interact with their existing Blockchain.info wallet, send funds, manage addresses etc. 4 | 5 | Example usage: 6 | 7 | ```java 8 | 9 | package test; 10 | import info.blockchain.api.wallet.*; 11 | 12 | public class App 13 | { 14 | public static void main(String[] args) throws Exception 15 | { 16 | 17 | CreateWalletResponse wallet = Wallet.create( 18 | "http://localhost:3000/", 19 | "YOUR_SUPER_SECURE_PASSWORD", 20 | "YOUR_API_CODE"); 21 | 22 | System.out.println(wallet.getIdentifier()); 23 | 24 | Wallet wallet = new Wallet( 25 | "http://localhost:3000/", 26 | "YOU_API_CODE", 27 | "YOUR_GUID", 28 | "YOUR_SUPER_SECURE_PASSWORD"); 29 | 30 | // get an address from your wallet and include only transactions with up to 3 31 | // confirmations in the balance 32 | Address addr = wallet.getAddress("1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh", 3); 33 | System.out.println(String.format("The balance is %s", addr.getBalance())); 34 | 35 | // send 0.2 bitcoins with a custom fee of 0.01 BTC and a note 36 | // public notes require a minimum transaction size of 0.005 BTC 37 | PaymentResponse payment = wallet.send("1dice6YgEVBf88erBFra9BHf6ZMoyvG88", 20000000L, null, 1000000L); 38 | System.out.println(String.format("The payment TX hash is %s", payment.getTxHash())); 39 | 40 | long totalBalance = wallet.getBalance(); 41 | System.out.println(String.format("The total wallet balance is %s", totalBalance)); 42 | 43 | // list all addresses and their balances 44 | List
addresses = wallet.listAddresses(); 45 | for (Address a : addresses) 46 | { 47 | System.out.println(String.format("The address %s has a balance of %s satoshi", 48 | a.getAddress(), a.getBalance())); 49 | } 50 | 51 | // archive an old address 52 | wallet.archiveAddress("1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh"); 53 | 54 | // create a new address and attach a label to it 55 | Address newAddr = wallet.newAddress("test label 123"); 56 | System.out.println(String.format("The new address is %s", newAddr.getAddress())); 57 | } 58 | } 59 | 60 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | info.blockchain 6 | api 7 | 2.0.2 8 | jar 9 | 10 | api 11 | https://blockchain.info/api 12 | 13 | 14 | UTF-8 15 | github 16 | 1.6 17 | 1.6 18 | 19 | 20 | 21 | 22 | internal.repo 23 | Temporary Staging Repository 24 | file://${project.build.directory}/mvn-repo 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | com.google.code.gson 33 | gson 34 | 2.3 35 | 36 | 37 | 38 | com.squareup.okhttp 39 | okhttp 40 | 2.6.0 41 | 42 | 43 | 44 | org.apache.commons 45 | commons-lang3 46 | 3.4 47 | 48 | 49 | 50 | junit 51 | junit 52 | 4.12 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | maven-deploy-plugin 61 | 2.8.2 62 | 63 | internal.repo::default::file://${project.build.directory}/mvn-repo 64 | 65 | 66 | 67 | 68 | org.jacoco 69 | jacoco-maven-plugin 70 | 0.7.5.201505241946 71 | 72 | 73 | 74 | prepare-agent 75 | 76 | 77 | 78 | report 79 | prepare-package 80 | 81 | report 82 | 83 | 84 | 85 | 86 | 87 | 88 | com.github.github 89 | site-maven-plugin 90 | 0.12 91 | 92 | 93 | Maven artifacts for ${project.version} 94 | 95 | true 96 | 97 | ${project.build.directory}/mvn-repo 98 | 99 | true 100 | 101 | refs/heads/mvn-repo 102 | **/* 103 | 104 | api-v1-client-java 105 | 106 | blockchain 107 | 108 | 109 | 110 | 111 | 112 | site 113 | 114 | deploy 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/APIException.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | /** 4 | * The class `APIException` represents a failed call to the Blockchain API. Whenever 5 | * the server is unable to process a request (usually due to parameter validation errors), 6 | * an instance of this class is thrown. 7 | */ 8 | public class APIException extends Exception { 9 | private static final long serialVersionUID = -7731961787745059713L; 10 | 11 | public APIException (String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/HttpClient.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | import java.io.*; 4 | import java.net.HttpURLConnection; 5 | import java.net.URL; 6 | import java.net.URLEncoder; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | 10 | /** 11 | * This is a utility class for performing API calls using GET and POST requests. It is 12 | * possible to override these calls by implementing HttpClientInterface. Add the new 13 | * implementation via setCustomHttpClient(...), such that it will get used globally. 14 | */ 15 | public class HttpClient implements HttpClientInterface { 16 | private static final String BASE_URL = "https://blockchain.info/"; 17 | 18 | public volatile static int TIMEOUT_MS = 10000; 19 | 20 | private static HttpClientInterface instance; 21 | 22 | public synchronized static HttpClientInterface getInstance () { 23 | if (instance == null) { 24 | // Thread Safe. Might be costly operation in some case 25 | synchronized (HttpClient.class) { 26 | if (instance == null) { 27 | instance = new HttpClient(); 28 | } 29 | } 30 | } 31 | return instance; 32 | } 33 | 34 | public static void setCustomHttpClient (HttpClientInterface httpClient) { 35 | instance = httpClient; 36 | } 37 | 38 | /** 39 | * Perform a GET request on a Blockchain.info API resource. 40 | * 41 | * @param resource Resource path after https://blockchain.info/api/ 42 | * @param params Map containing request parameters 43 | * @return String response 44 | * @throws APIException If the server returns an error 45 | */ 46 | public String get (String resource, Map params) throws APIException, IOException { 47 | return openURL(BASE_URL, resource, params, "GET"); 48 | } 49 | 50 | public String get (String baseURL, String resource, Map params) throws APIException, IOException { 51 | return openURL(baseURL, resource, params, "GET"); 52 | } 53 | 54 | /** 55 | * Perform a POST request on a Blockchain.info API resource. 56 | * 57 | * @param resource Resource path after https://blockchain.info/api/ 58 | * @param params Map containing request parameters 59 | * @return String response 60 | * @throws APIException If the server returns an error 61 | * @throws IOException If the server is not reachable 62 | */ 63 | public String post (String resource, Map params) throws APIException, IOException { 64 | return openURL(BASE_URL, resource, params, "POST"); 65 | } 66 | 67 | public String post (String baseURL, String resource, Map params) throws APIException, IOException { 68 | return openURL(baseURL, resource, params, "POST"); 69 | } 70 | 71 | private static String openURL (String baseURL, String resource, Map params, String requestMethod) throws APIException, IOException { 72 | String encodedParams = urlEncodeParams(params); 73 | URL url = null; 74 | APIException apiException = null; 75 | IOException ioException = null; 76 | 77 | String responseStr = null; 78 | 79 | if (requestMethod.equals("GET")) { 80 | if (encodedParams.isEmpty()) { 81 | url = new URL(baseURL + resource); 82 | } else { 83 | url = new URL(baseURL + resource + '?' + encodedParams); 84 | } 85 | } else if (requestMethod.equals("POST")) { 86 | url = new URL(baseURL + resource); 87 | } 88 | 89 | HttpURLConnection conn = null; 90 | 91 | try { 92 | conn = (HttpURLConnection) url.openConnection(); 93 | conn.setRequestMethod(requestMethod); 94 | conn.setConnectTimeout(TIMEOUT_MS); 95 | 96 | if (requestMethod.equals("POST")) { 97 | byte[] postBytes = encodedParams.getBytes("UTF-8"); 98 | conn.setDoOutput(true); 99 | conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 100 | conn.setRequestProperty("Content-Length", String.valueOf(postBytes.length)); 101 | conn.getOutputStream().write(postBytes); 102 | conn.getOutputStream().close(); 103 | } 104 | 105 | if (conn.getResponseCode() != 200) { 106 | apiException = new APIException(inputStreamToString(conn.getErrorStream())); 107 | } else { 108 | responseStr = inputStreamToString(conn.getInputStream()); 109 | } 110 | } catch (IOException e) { 111 | ioException = e; 112 | } finally { 113 | try { 114 | if (apiException != null) { 115 | conn.getErrorStream().close(); 116 | } 117 | conn.getInputStream().close(); 118 | } catch (Exception ex) { 119 | } 120 | 121 | if (ioException != null) { 122 | throw ioException; 123 | } 124 | 125 | if (apiException != null) { 126 | throw apiException; 127 | } 128 | } 129 | 130 | return responseStr; 131 | } 132 | 133 | private static String urlEncodeParams (Map params) { 134 | String result = ""; 135 | 136 | if (params != null && params.size() > 0) { 137 | try { 138 | StringBuilder data = new StringBuilder(); 139 | for (Entry kvp : params.entrySet()) { 140 | if (data.length() > 0) { 141 | data.append('&'); 142 | } 143 | 144 | data.append(URLEncoder.encode(kvp.getKey(), "UTF-8")); 145 | data.append('='); 146 | data.append(URLEncoder.encode(kvp.getValue(), "UTF-8")); 147 | } 148 | result = data.toString(); 149 | } catch (UnsupportedEncodingException e) { 150 | } 151 | } 152 | 153 | return result; 154 | } 155 | 156 | private static String inputStreamToString (InputStream is) throws IOException { 157 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 158 | 159 | StringBuilder responseStringBuilder = new StringBuilder(); 160 | String line = ""; 161 | 162 | while ((line = reader.readLine()) != null) { 163 | responseStringBuilder.append(line); 164 | } 165 | 166 | reader.close(); 167 | 168 | return responseStringBuilder.toString(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/HttpClientInterface.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | /** 7 | * This is a utility class for performing API calls using GET and POST requests. It is 8 | * possible to override these calls with your own implementation by overriding the 'get' 9 | * and 'post' methods. 10 | */ 11 | public interface HttpClientInterface { 12 | 13 | String get (String resource, Map params) throws APIException, IOException; 14 | 15 | String get (String baseURL, String resource, Map params) throws APIException, IOException; 16 | 17 | String post (String resource, Map params) throws APIException, IOException; 18 | 19 | String post (String baseURL, String resource, Map params) throws APIException, IOException; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/OkClient.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | import com.squareup.okhttp.*; 4 | 5 | import java.io.IOException; 6 | import java.net.MalformedURLException; 7 | import java.util.Map; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * This class implements the {@link HttpClientInterface} using OkHttp. 12 | * OkHttp handles Gzip, connection pooling, caching and retries out of the box. 13 | * The library also provides a set of fluent builders for creating requests and responses. 14 | * For more information about OkHttp visit http://square.github.io/okhttp/ 15 | */ 16 | public class OkClient implements HttpClientInterface { 17 | 18 | public static final String URL_SCHEME = "https"; 19 | public static final String URL_HOST = "blockchain.info"; 20 | public static final int TIMEOUT_MS = 10000; 21 | 22 | private static OkHttpClient okHttpClientInstance = null; 23 | private static OkClient okClientInstance = null; 24 | 25 | /** 26 | * Private constructor to prevent instantiation. 27 | */ 28 | private OkClient() { 29 | //prevent instantiation through reflection. 30 | if (okClientInstance != null) { 31 | throw new IllegalStateException("Already initialized."); 32 | } 33 | } 34 | 35 | /** 36 | * OkClient singleton 37 | */ 38 | public synchronized static OkClient getOkClientInstance() { 39 | if (okClientInstance == null) { 40 | okClientInstance = new OkClient(); 41 | } 42 | return okClientInstance; 43 | } 44 | 45 | /** 46 | * OkHttpClient singleton 47 | */ 48 | private synchronized static OkHttpClient getOkHttpClientInstance() { 49 | if (okHttpClientInstance == null) { 50 | okHttpClientInstance = new OkHttpClient(); 51 | okHttpClientInstance.setConnectTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS); 52 | } 53 | return okHttpClientInstance; 54 | } 55 | 56 | /** 57 | * This method makes a get request to a specific API resource. 58 | * Request is built using the OkHttp {@link HttpUrl} builder. 59 | * 60 | * @param baseURL The API base URL 61 | * @param resource The API resource being requested. 62 | * @param params A set of params sent to the resource 63 | * @return Returns a response as a {@link String} 64 | * @throws IOException Thrown if the request was unsuccessful. 65 | */ 66 | @Override 67 | public String get(String baseURL, String resource, Map params) throws IOException { 68 | HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource); 69 | 70 | for (String paramName : params.keySet()) { 71 | url.addEncodedQueryParameter(paramName, params.get(paramName)); 72 | } 73 | 74 | return getInternal(url.build()); 75 | } 76 | 77 | /** 78 | * This method makes a get request to a specific API resource. 79 | * Request is built using the OkHttp {@link HttpUrl} builder. 80 | * 81 | * @param resource The API resource being requested. 82 | * @param params A set of params sent to the resource 83 | * @return Returns a response as a {@link String} 84 | * @throws IOException Thrown if the request was unsuccessful. 85 | */ 86 | @Override 87 | public String get(String resource, Map params) throws IOException { 88 | HttpUrl.Builder url = getHttpUrlBuilder(resource); 89 | 90 | for (String paramName : params.keySet()) { 91 | url.addEncodedQueryParameter(paramName, params.get(paramName)); 92 | } 93 | 94 | return getInternal(url.build()); 95 | } 96 | 97 | private String getInternal(HttpUrl url) throws IOException { 98 | Request request = new Request.Builder() 99 | .url(url) 100 | .build(); 101 | 102 | return makeRequest(request); 103 | } 104 | 105 | /** 106 | * This method makes a post request to a specific API resource. 107 | * Request is built using the OkHttp {@link HttpUrl} builder. 108 | * Request body is built using the OkHttp {@link FormEncodingBuilder} builder . 109 | * 110 | * @param baseURL The API base URL 111 | * @param resource The API resource being requested. 112 | * @param params A set of params sent to the resource 113 | * @return Returns a response as a {@link String} 114 | * @throws IOException Thrown if the request was unsuccessful. 115 | */ 116 | @Override 117 | public String post(String baseURL, String resource, Map params) throws IOException { 118 | HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource); 119 | 120 | return postInternal(url.build(), params); 121 | } 122 | 123 | /** 124 | * This method makes a post request to a specific API resource. 125 | * Request is built using the OkHttp {@link HttpUrl} builder. 126 | * Request body is built using the OkHttp {@link FormEncodingBuilder} builder . 127 | * 128 | * @param resource The API resource being requested. 129 | * @param params A set of params sent to the resource 130 | * @return Returns a response as a {@link String} 131 | * @throws IOException Thrown if the request was unsuccessful. 132 | */ 133 | @Override 134 | public String post(String resource, Map params) throws IOException { 135 | HttpUrl.Builder url = getHttpUrlBuilder(resource); 136 | 137 | return postInternal(url.build(), params); 138 | } 139 | 140 | private String postInternal(HttpUrl url, Map params) throws IOException { 141 | FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder(); 142 | for (String paramName : params.keySet()) { 143 | formEncodingBuilder.addEncoded(paramName, params.get(paramName)); 144 | } 145 | 146 | Request request = new Request.Builder() 147 | .url(url) 148 | .post(formEncodingBuilder.build()) 149 | .build(); 150 | 151 | return makeRequest(request); 152 | } 153 | 154 | private String makeRequest(Request request) throws IOException { 155 | Response response = getOkHttpClientInstance().newCall(request).execute(); 156 | if (isNotSuccessfulResponse(response)) 157 | throw new IOException(String.format("Unsuccessful call to %s Response: %s", 158 | request.urlString(), 159 | response)); 160 | 161 | return response.body().string(); 162 | } 163 | 164 | private HttpUrl.Builder getHttpUrlBuilder(String resource) { 165 | HttpUrl.Builder url = new HttpUrl.Builder(); 166 | url.scheme(URL_SCHEME) 167 | .host(URL_HOST) 168 | .addPathSegment(resource); 169 | 170 | return url; 171 | } 172 | 173 | private HttpUrl.Builder getHttpUrlBuilder(String baseURL, String resource) throws MalformedURLException { 174 | HttpUrl url = HttpUrl.parse(baseURL); 175 | if (url == null) { 176 | throw new MalformedURLException(); 177 | } 178 | 179 | HttpUrl.Builder urlBuilder = url.newBuilder(); 180 | urlBuilder.addPathSegment(resource); 181 | 182 | return urlBuilder; 183 | } 184 | 185 | private boolean isNotSuccessfulResponse(Response response) { 186 | return !response.isSuccessful(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/Util.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | public class Util { 4 | public static final int SATOSHI_IN_BTC = 100000000; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/BlockExplorer.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import info.blockchain.api.APIException; 7 | import info.blockchain.api.HttpClient; 8 | import info.blockchain.api.blockexplorer.entity.*; 9 | import org.apache.commons.lang3.StringUtils; 10 | 11 | import java.io.IOException; 12 | import java.util.*; 13 | 14 | /** 15 | * The BlockExplorer class reflects the functionality documented at 16 | * https://blockchain.info/api/blockchain_api. It can be used to query the block chain, 17 | * fetch block, transaction and address data, get unspent outputs for an address etc. 18 | */ 19 | public class BlockExplorer { 20 | private final String apiCode; 21 | 22 | public BlockExplorer () { 23 | this(null); 24 | } 25 | 26 | /** 27 | * @param apiCode Blockchain.info API code (optional, nullable) 28 | */ 29 | public BlockExplorer (String apiCode) { 30 | this.apiCode = apiCode; 31 | } 32 | 33 | /** 34 | * Gets a single transaction based on a transaction index. 35 | * 36 | * @param txIndex Transaction index 37 | * @return An instance of the {@link Transaction} class 38 | * @throws APIException If the server returns an error 39 | * @deprecated As of 1.1.5, will be removed in future releases 40 | */ 41 | @Deprecated 42 | public Transaction getTransaction (long txIndex) throws APIException, IOException { 43 | return getTransaction(String.valueOf(txIndex)); 44 | } 45 | 46 | /** 47 | * Gets a single transaction based on a transaction hash. 48 | * 49 | * @param txHash Transaction hash 50 | * @return An instance of the {@link Transaction} class 51 | * @throws APIException If the server returns an error 52 | */ 53 | public Transaction getTransaction (String txHash) throws APIException, IOException { 54 | String response = HttpClient.getInstance().get("rawtx/" + txHash, buildBasicRequest()); 55 | JsonObject txJson = new JsonParser().parse(response).getAsJsonObject(); 56 | return new Transaction(txJson); 57 | } 58 | 59 | /** 60 | * Gets a single block based on a block index. 61 | * 62 | * @param blockIndex Block index 63 | * @return An instance of the {@link Block} class 64 | * @throws APIException If the server returns an error 65 | * @deprecated As of 1.1.5, will be removed in future releases 66 | */ 67 | @Deprecated 68 | public Block getBlock (long blockIndex) throws APIException, IOException { 69 | return getBlock(String.valueOf(blockIndex)); 70 | } 71 | 72 | /** 73 | * Gets a single block based on a block hash. 74 | * 75 | * @param blockHash Block hash 76 | * @return An instance of the {@link Block} class 77 | * @throws APIException If the server returns an error 78 | */ 79 | public Block getBlock (String blockHash) throws APIException, IOException { 80 | String response = HttpClient.getInstance().get("rawblock/" + blockHash, buildBasicRequest()); 81 | JsonObject blockJson = new JsonParser().parse(response).getAsJsonObject(); 82 | return new Block(blockJson); 83 | } 84 | 85 | /** 86 | * Gets data for a single address. 87 | * 88 | * @param address Base58check or hash160 address string 89 | * @param filter the filter for transactions selection, use null to indicate default 90 | * @param limit an integer to limit number of transactions to display, use null to indicate default 91 | * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default 92 | * @return An instance of the {@link Address} class 93 | * @throws APIException If the server returns an error 94 | */ 95 | public Address getAddress (String address, FilterType filter, Integer limit, Integer offset) throws APIException, IOException { 96 | Map params = buildBasicRequest(); 97 | if (filter != null) { 98 | params.put("filter", filter.getFilterInt().toString()); 99 | } 100 | if (limit != null) { 101 | params.put("limit", limit.toString()); 102 | } 103 | if (offset != null) { 104 | params.put("offset", offset.toString()); 105 | } 106 | String response = HttpClient.getInstance().get("rawaddr/" + address, params); 107 | JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject(); 108 | 109 | return new Address(addrJson); 110 | } 111 | 112 | /** 113 | * Gets data for a single address. 114 | * 115 | * @param address Base58check or hash160 address string 116 | * @return An instance of the {@link Address} class 117 | * @throws APIException If the server returns an error 118 | */ 119 | public Address getAddress (String address) throws APIException, IOException { 120 | return getAddress(address, null, null, null); 121 | } 122 | 123 | /** 124 | * Gets a list of blocks at the specified height. Normally, only one block will be 125 | * returned, but in case of a chain fork, multiple blocks may be present. 126 | * 127 | * @param height Block height 128 | * @return A list of blocks at the specified height 129 | * @throws APIException If the server returns an error 130 | */ 131 | public List getBlocksAtHeight (long height) throws APIException, IOException { 132 | List blocks = new ArrayList(); 133 | 134 | String response = HttpClient.getInstance().get("block-height/" + height, buildBasicRequest()); 135 | JsonObject blocksJson = new JsonParser().parse(response).getAsJsonObject(); 136 | 137 | for (JsonElement blockElem : blocksJson.get("blocks").getAsJsonArray()) { 138 | blocks.add(new Block(blockElem.getAsJsonObject())); 139 | } 140 | 141 | return blocks; 142 | } 143 | 144 | /** 145 | * Returns list of unspent outputs. 146 | * 147 | * @param addressList a list of Base58 or xpub addresses 148 | * @param confirms an integer for minimum confirms of the outputs, use null to indicate default 149 | * @param limit an integer to limit number of transactions to display, use null to indicate default 150 | * @return A list of unspent outputs for the specified address 151 | * @throws APIException If the server returns an error 152 | */ 153 | public List getUnspentOutputs (List addressList, Integer confirms, Integer limit) throws APIException, IOException { 154 | List outputs = new ArrayList(); 155 | 156 | Map params = buildBasicRequest(); 157 | String pipedAddresses = StringUtils.join(addressList, "|"); 158 | params.put("active", pipedAddresses); 159 | if (confirms != null) { 160 | params.put("confirmations", confirms.toString()); 161 | } 162 | if (limit != null) { 163 | params.put("limit", limit.toString()); 164 | } 165 | 166 | String response = null; 167 | try { 168 | response = HttpClient.getInstance().get("unspent", params); 169 | } catch (APIException e) { 170 | // The server endpoint will return error if no output, however this should be a valid situation so we return an empty result. 171 | if (e.getMessage().equals("No free outputs to spend")) { 172 | return outputs; 173 | } 174 | } 175 | 176 | JsonObject outsJson = new JsonParser().parse(response).getAsJsonObject(); 177 | for (JsonElement outElem : outsJson.get("unspent_outputs").getAsJsonArray()) { 178 | outputs.add(new UnspentOutput(outElem.getAsJsonObject())); 179 | } 180 | 181 | return outputs; 182 | } 183 | 184 | /** 185 | * Gets unspent outputs for a single address. 186 | * 187 | * @param address Base58check or hash160 address string 188 | * @return A list of unspent outputs for the specified address 189 | * @throws APIException If the server returns an error 190 | */ 191 | public List getUnspentOutputs (String address) throws APIException, IOException { 192 | return getUnspentOutputs(Arrays.asList(address), null, null); 193 | } 194 | 195 | /** 196 | * Gets the latest block on the main chain (simplified representation). 197 | * 198 | * @return An instance of the {@link LatestBlock} class 199 | * @throws APIException If the server returns an error 200 | */ 201 | public LatestBlock getLatestBlock () throws APIException, IOException { 202 | String response = HttpClient.getInstance().get("latestblock", buildBasicRequest()); 203 | JsonObject blockObj = new JsonParser().parse(response).getAsJsonObject(); 204 | return new LatestBlock(blockObj); 205 | } 206 | 207 | /** 208 | * Gets a list of currently unconfirmed transactions. 209 | * 210 | * @return A list of unconfirmed {@link Transaction} objects 211 | * @throws APIException If the server returns an error 212 | */ 213 | public List getUnconfirmedTransactions () throws APIException, IOException { 214 | List transactions = new ArrayList(); 215 | 216 | String response = HttpClient.getInstance().get("unconfirmed-transactions", buildBasicRequest()); 217 | JsonObject txList = new JsonParser().parse(response).getAsJsonObject(); 218 | 219 | for (JsonElement txElem : txList.get("txs").getAsJsonArray()) { 220 | JsonObject txObj = txElem.getAsJsonObject(); 221 | transactions.add(new Transaction(txObj, -1, txObj.get("double_spend").getAsBoolean())); 222 | } 223 | 224 | return transactions; 225 | } 226 | 227 | /** 228 | * Gets a list of blocks mined today by all pools since 00:00 UTC. 229 | * 230 | * @return A list of {@link SimpleBlock} objects 231 | * @throws APIException APIException If the server returns an error 232 | */ 233 | public List getBlocks () throws APIException, IOException { 234 | return getBlocks(null); 235 | } 236 | 237 | /** 238 | * Gets a list of blocks mined on a specific day. 239 | * 240 | * @param timestamp Unix timestamp (without milliseconds) that falls between 241 | * 00:00 UTC and 23:59 UTC of the desired day. 242 | * @return A list of {@link SimpleBlock} objects 243 | */ 244 | public List getBlocks (long timestamp) throws APIException, IOException { 245 | return getBlocks(String.valueOf(timestamp * 1000)); 246 | } 247 | 248 | /** 249 | * Gets a list of recent blocks by a specific mining pool. 250 | * 251 | * @param poolName Name of the mining pool 252 | * @return A list of {@link SimpleBlock} objects 253 | * @throws APIException If the server returns an error 254 | */ 255 | public List getBlocks (String poolName) throws APIException, IOException { 256 | List blocks = new ArrayList(); 257 | poolName = poolName == null ? "" : poolName; 258 | 259 | String response = HttpClient.getInstance().get("blocks/" + poolName, buildBasicRequest()); 260 | JsonObject blockList = new JsonParser().parse(response).getAsJsonObject(); 261 | 262 | for (JsonElement blockElem : blockList.get("blocks").getAsJsonArray()) { 263 | blocks.add(new SimpleBlock(blockElem.getAsJsonObject())); 264 | } 265 | 266 | return blocks; 267 | } 268 | 269 | /** 270 | * Returns the address balance summary for each address provided 271 | * 272 | * @param addressList base58 or xpub addresses 273 | * @param filter the filter for transactions selection, use null to indicate default 274 | * @return a map of (address, {@link Balance}) 275 | */ 276 | public Map getBalance(List addressList, FilterType filter) throws APIException, IOException { 277 | Map params = buildBasicRequest(); 278 | String pipedAddresses = StringUtils.join(addressList, "|"); 279 | params.put("active", pipedAddresses); 280 | if (filter != null) { 281 | params.put("filter", filter.getFilterInt().toString()); 282 | } 283 | 284 | String response = HttpClient.getInstance().get("balance", params); 285 | JsonObject balanceMap = new JsonParser().parse(response).getAsJsonObject(); 286 | 287 | Map balances = new HashMap(); 288 | for (String address : addressList) { 289 | JsonObject balance = balanceMap.getAsJsonObject(address); 290 | balances.put(address, new Balance(balance)); 291 | } 292 | 293 | return balances; 294 | } 295 | 296 | /** 297 | * Returns an aggregated summary on all addresses provided. 298 | * 299 | * @param addressList a list of Base58 or xpub addresses 300 | * @param filter the filter for transactions selection, use null to indicate default 301 | * @param limit an integer to limit number of transactions to display, use null to indicate default 302 | * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default 303 | * @return An instance of the {@link MultiAddress} class 304 | * @throws APIException If the server returns an error 305 | */ 306 | public MultiAddress getMultiAddress(List addressList, FilterType filter, Integer limit, Integer offset) throws APIException, IOException { 307 | Map params = buildBasicRequest(); 308 | String pipedAddresses = StringUtils.join(addressList, "|"); 309 | params.put("active", pipedAddresses); 310 | if (filter != null) { 311 | params.put("filter", filter.getFilterInt().toString()); 312 | } 313 | if (limit != null) { 314 | params.put("n", limit.toString()); 315 | } 316 | if (offset != null) { 317 | params.put("offset", offset.toString()); 318 | } 319 | 320 | String response = HttpClient.getInstance().get("multiaddr", params); 321 | JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject(); 322 | 323 | return new MultiAddress(addrJson); 324 | } 325 | 326 | /** 327 | * Returns xpub summary on a xpub provided, with its overall balance and its transactions. 328 | * 329 | * @param xpub a xpub address 330 | * @param filter the filter for transactions selection, use null to indicate default 331 | * @param limit an integer to limit number of transactions to display, use null to indicate default 332 | * @param offset an integer to set number of transactions to skip when fetch 333 | * @return {@link XpubFull} an object to represent the xpub summary 334 | */ 335 | public XpubFull getXpub(String xpub, FilterType filter, Integer limit, Integer offset) throws APIException, IOException { 336 | MultiAddress multiAddress = getMultiAddress(Arrays.asList(xpub), filter, limit, offset); 337 | 338 | return new XpubFull(multiAddress.getAddresses().get(0), multiAddress.getTxs()); 339 | } 340 | 341 | private Map buildBasicRequest () { 342 | Map params = new HashMap(); 343 | 344 | params.put("format", "json"); 345 | if (apiCode != null) { 346 | params.put("api_code", apiCode); 347 | } 348 | 349 | return params; 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Address.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Representation of an address. 11 | */ 12 | public class Address { 13 | private String hash160; 14 | private String address; 15 | private long totalReceived; 16 | private long totalSent; 17 | private long finalBalance; 18 | private int txCount; 19 | private List transactions; 20 | 21 | public Address (String hash160, String address, long totalReceived, long totalSent, long finalBalance, int txCount, List transactions) { 22 | this.hash160 = hash160; 23 | this.address = address; 24 | this.totalReceived = totalReceived; 25 | this.totalSent = totalSent; 26 | this.finalBalance = finalBalance; 27 | this.txCount = txCount; 28 | this.transactions = transactions; 29 | } 30 | 31 | public Address (JsonObject a) { 32 | 33 | this( 34 | a.has("hash160") ? a.get("hash160").getAsString() : "", 35 | a.has("address") ? a.get("address").getAsString() : "", 36 | a.has("total_received") ? a.get("total_received").getAsLong() : 0, 37 | a.has("total_sent") ? a.get("total_sent").getAsLong() : 0, 38 | a.has("final_balance") ? a.get("final_balance").getAsLong() : 0, 39 | a.has("n_tx") ? a.get("n_tx").getAsInt() : 0, 40 | null); 41 | 42 | transactions = new ArrayList(); 43 | for (JsonElement txElem : a.get("txs").getAsJsonArray()) { 44 | JsonObject addrObj = txElem.getAsJsonObject(); 45 | transactions.add(new Transaction(addrObj)); 46 | } 47 | } 48 | 49 | @Override 50 | public boolean equals (Object o) { 51 | if (this == o) { 52 | return true; 53 | } 54 | if (o == null || getClass() != o.getClass()) { 55 | return false; 56 | } 57 | 58 | Address address1 = (Address) o; 59 | 60 | if (totalReceived != address1.totalReceived) { 61 | return false; 62 | } 63 | if (totalSent != address1.totalSent) { 64 | return false; 65 | } 66 | if (finalBalance != address1.finalBalance) { 67 | return false; 68 | } 69 | if (!hash160.equals(address1.hash160)) { 70 | return false; 71 | } 72 | return address.equals(address1.address); 73 | 74 | } 75 | 76 | @Override 77 | public int hashCode () { 78 | int result = hash160.hashCode(); 79 | result = 31 * result + address.hashCode(); 80 | result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32)); 81 | result = 31 * result + (int) (totalSent ^ (totalSent >>> 32)); 82 | result = 31 * result + (int) (finalBalance ^ (finalBalance >>> 32)); 83 | return result; 84 | } 85 | 86 | /** 87 | * @return Hash160 representation of the address 88 | */ 89 | public String getHash160 () { 90 | return hash160; 91 | } 92 | 93 | /** 94 | * @return Base58Check representation of the address 95 | */ 96 | public String getAddress () { 97 | return address; 98 | } 99 | 100 | /** 101 | * @return Total amount received (in satoshi) 102 | */ 103 | public long getTotalReceived () { 104 | return totalReceived; 105 | } 106 | 107 | /** 108 | * @return Total amount sent (in satoshi) 109 | */ 110 | public long getTotalSent () { 111 | return totalSent; 112 | } 113 | 114 | /** 115 | * @return Final balance of the address (in satoshi) 116 | */ 117 | public long getFinalBalance () { 118 | return finalBalance; 119 | } 120 | 121 | /** 122 | * @return Original number of transactions before filtering and limiting 123 | */ 124 | public int getTxCount () { 125 | return txCount; 126 | } 127 | 128 | /** 129 | * @return List of transactions associated with this address 130 | */ 131 | public List getTransactions () { 132 | return transactions; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/AddressSummary.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class AddressSummary { 6 | 7 | private String address; 8 | private int txCount; 9 | private long totalReceived; 10 | private long totalSent; 11 | private long finalBalance; 12 | private int changeIndex; 13 | private int accountIndex; 14 | private int gapLimit; 15 | 16 | public AddressSummary (String address, int txCount, long totalReceived, long totalSent, long finalBalance, 17 | int changeIndex, int accountIndex, int gapLimit) { 18 | this.address = address; 19 | this.txCount = txCount; 20 | this.totalReceived = totalReceived; 21 | this.totalSent = totalSent; 22 | this.finalBalance = finalBalance; 23 | this.changeIndex = changeIndex; 24 | this.accountIndex = accountIndex; 25 | this.gapLimit = gapLimit; 26 | } 27 | 28 | public AddressSummary (JsonObject json) { 29 | this( 30 | json.has("address") ? json.get("address").getAsString() : "", 31 | json.has("n_tx") ? json.get("n_tx").getAsInt() : 0, 32 | json.has("total_received") ? json.get("total_received").getAsLong() : 0, 33 | json.has("total_sent") ? json.get("total_sent").getAsLong() : 0, 34 | json.has("final_balance") ? json.get("final_balance").getAsLong() : 0, 35 | json.has("change_index") ? json.get("change_index").getAsInt() : 0, 36 | json.has("account_index") ? json.get("account_index").getAsInt() : 0, 37 | json.has("gap_limit") ? json.get("gap_limit").getAsInt() : 0 38 | ); 39 | } 40 | 41 | public String getAddress() { 42 | return address; 43 | } 44 | 45 | public int getTxCount() { 46 | return txCount; 47 | } 48 | 49 | public void setTxCount(int txCount) { 50 | this.txCount = txCount; 51 | } 52 | 53 | public long getTotalReceived() { 54 | return totalReceived; 55 | } 56 | 57 | public long getTotalSent() { 58 | return totalSent; 59 | } 60 | 61 | public long getFinalBalance() { 62 | return finalBalance; 63 | } 64 | 65 | public int getChangeIndex() { 66 | return changeIndex; 67 | } 68 | 69 | public int getAccountIndex() { 70 | return accountIndex; 71 | } 72 | 73 | public void setFinalBalance(long final_balance) { 74 | this.finalBalance = final_balance; 75 | } 76 | 77 | public void setAddress(String address) { 78 | this.address = address; 79 | } 80 | 81 | public void setTotalReceived(long total_received) { 82 | this.totalReceived = total_received; 83 | } 84 | 85 | public void setTotalSent(long total_sent) { 86 | this.totalSent = total_sent; 87 | } 88 | 89 | public void setChangeIndex(int change_index) { 90 | this.changeIndex = change_index; 91 | } 92 | 93 | public void setAccountIndex(int account_index) { 94 | this.accountIndex = account_index; 95 | } 96 | 97 | public int getGapLimit () { 98 | return gapLimit; 99 | } 100 | 101 | public void setGapLimit (int gapLimit) { 102 | this.gapLimit = gapLimit; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Balance.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class Balance { 6 | 7 | private long finalBalance; 8 | private long txCount; 9 | private long totalReceived; 10 | 11 | public Balance (long finalBalance, long txCount, long totalReceived) { 12 | this.finalBalance = finalBalance; 13 | this.txCount = txCount; 14 | this.totalReceived = totalReceived; 15 | } 16 | 17 | public Balance (JsonObject json) { 18 | 19 | this( 20 | json.has("final_balance") ? json.get("final_balance").getAsLong() : 0, 21 | json.has("n_tx") ? json.get("n_tx").getAsLong() : 0, 22 | json.has("total_received") ? json.get("total_received").getAsLong() : 0 23 | ); 24 | } 25 | 26 | public long getFinalBalance() { 27 | return finalBalance; 28 | } 29 | 30 | public long getTxCount() { 31 | return txCount; 32 | } 33 | 34 | public long getTotalReceived() { 35 | return totalReceived; 36 | } 37 | 38 | public void setFinalBalance(long final_balance) { 39 | this.finalBalance = final_balance; 40 | } 41 | 42 | public void setTxCount(long n_tx) { 43 | this.txCount = n_tx; 44 | } 45 | 46 | public void setTotalReceived(long total_received) { 47 | this.totalReceived = total_received; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Block.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * This class is a full representation of a block. For simpler representations, see 11 | * `SimpleBlock` and `LatestBlock`. 12 | */ 13 | public class Block extends SimpleBlock { 14 | private int version; 15 | private String previousBlockHash; 16 | private String merkleRoot; 17 | private long bits; 18 | private long fees; 19 | private long nonce; 20 | private long size; 21 | private long index; 22 | private long receivedTime; 23 | private String relayedBy; 24 | private List transactions; 25 | 26 | public Block (long height, String hash, long time, boolean mainChain, int version, String previousBlockHash, String merkleRoot, long bits, long fees, long nonce, long size, long index, long receivedTime, String relayedBy, List transactions) { 27 | super(height, hash, time, mainChain); 28 | this.version = version; 29 | this.previousBlockHash = previousBlockHash; 30 | this.merkleRoot = merkleRoot; 31 | this.bits = bits; 32 | this.fees = fees; 33 | this.nonce = nonce; 34 | this.size = size; 35 | this.index = index; 36 | this.receivedTime = receivedTime; 37 | this.relayedBy = relayedBy; 38 | this.transactions = transactions; 39 | } 40 | 41 | public Block (JsonObject b) { 42 | this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), b.get("main_chain").getAsBoolean(), b.get("ver").getAsInt(), b.get("prev_block").getAsString(), b.get("mrkl_root").getAsString(), b.get("bits").getAsLong(), b.get("fee").getAsLong(), b.get("nonce").getAsLong(), b.get("size").getAsLong(), b.get("block_index").getAsLong(), b.has("received_time") ? b.get("received_time").getAsLong() : b.get("time").getAsLong(), b.has("relayed_by") ? b.get("relayed_by").getAsString() : null, null); 43 | 44 | transactions = new ArrayList(); 45 | for (JsonElement txElem : b.get("tx").getAsJsonArray()) { 46 | transactions.add(new Transaction(txElem.getAsJsonObject(), super.getHeight(), false)); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean equals (Object o) { 52 | if (this == o) { 53 | return true; 54 | } 55 | if (o == null || getClass() != o.getClass()) { 56 | return false; 57 | } 58 | if (!super.equals(o)) { 59 | return false; 60 | } 61 | 62 | Block block = (Block) o; 63 | 64 | if (version != block.version) { 65 | return false; 66 | } 67 | if (fees != block.fees) { 68 | return false; 69 | } 70 | if (nonce != block.nonce) { 71 | return false; 72 | } 73 | if (size != block.size) { 74 | return false; 75 | } 76 | if (index != block.index) { 77 | return false; 78 | } 79 | if (!previousBlockHash.equals(block.previousBlockHash)) { 80 | return false; 81 | } 82 | return merkleRoot.equals(block.merkleRoot); 83 | 84 | } 85 | 86 | @Override 87 | public int hashCode () { 88 | int result = version; 89 | result = 31 * result + previousBlockHash.hashCode(); 90 | result = 31 * result + merkleRoot.hashCode(); 91 | result = 31 * result + (int) (fees ^ (fees >>> 32)); 92 | result = 31 * result + (int) (nonce ^ (nonce >>> 32)); 93 | result = 31 * result + (int) (size ^ (size >>> 32)); 94 | result = 31 * result + (int) (index ^ (index >>> 32)); 95 | return result; 96 | } 97 | 98 | /** 99 | * @return Block version as specified by the protocol 100 | */ 101 | public int getVersion () { 102 | return version; 103 | } 104 | 105 | /** 106 | * @return Hash of the previous block 107 | */ 108 | public String getPreviousBlockHash () { 109 | return previousBlockHash; 110 | } 111 | 112 | /** 113 | * @return Merkle root of the block 114 | */ 115 | public String getMerkleRoot () { 116 | return merkleRoot; 117 | } 118 | 119 | /** 120 | * @return Representation of the difficulty target for this block 121 | */ 122 | public long getBits () { 123 | return bits; 124 | } 125 | 126 | /** 127 | * @return Total transaction fees from this block (in satoshi) 128 | */ 129 | public long getFees () { 130 | return fees; 131 | } 132 | 133 | /** 134 | * @return Block nonce 135 | */ 136 | public long getNonce () { 137 | return nonce; 138 | } 139 | 140 | /** 141 | * @return Serialized size of this block 142 | */ 143 | public long getSize () { 144 | return size; 145 | } 146 | 147 | /** 148 | * @return Index of this block 149 | */ 150 | public long getIndex () { 151 | return index; 152 | } 153 | 154 | /** 155 | * @return The time this block was received by Blockchain.info 156 | */ 157 | public long getReceivedTime () { 158 | return receivedTime; 159 | } 160 | 161 | /** 162 | * @return IP address that relayed the block 163 | */ 164 | public String getRelayedBy () { 165 | return relayedBy; 166 | } 167 | 168 | /** 169 | * @return Transactions in the block 170 | */ 171 | public List getTransactions () { 172 | return transactions; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/FilterType.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | public enum FilterType { 4 | All(4), 5 | ConfirmedOnly(5), 6 | RemoveUnspendable(6); 7 | 8 | private final Integer filterInt; 9 | 10 | FilterType (Integer filterInt) { 11 | this.filterInt = filterInt; 12 | } 13 | 14 | public Integer getFilterInt () { 15 | return filterInt; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Input.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | /** 6 | * Represents a transaction input. If the `previousOutput` object is null, this is a 7 | * coinbase input. 8 | */ 9 | public class Input { 10 | private Output previousOutput; 11 | private long sequence; 12 | private String scriptSignature; 13 | 14 | public Input (Output previousOutput, long sequence, String scriptSignature) { 15 | this.previousOutput = previousOutput; 16 | this.sequence = sequence; 17 | this.scriptSignature = scriptSignature; 18 | } 19 | 20 | public Input (JsonObject i) { 21 | if (i.has("prev_out") && !i.get("prev_out").isJsonNull()) { 22 | this.previousOutput = new Output(i.get("prev_out").getAsJsonObject(), true); 23 | } 24 | 25 | this.sequence = i.get("sequence").getAsLong(); 26 | this.scriptSignature = i.get("script").getAsString(); 27 | } 28 | 29 | /** 30 | * @return Previous output. If null, this is a coinbase input. 31 | */ 32 | public Output getPreviousOutput () { 33 | return previousOutput; 34 | } 35 | 36 | /** 37 | * @return Sequence number of the input 38 | */ 39 | public long getSequence () { 40 | return sequence; 41 | } 42 | 43 | /** 44 | * @return Script signature 45 | */ 46 | public String getScriptSignature () { 47 | return scriptSignature; 48 | } 49 | 50 | @Override 51 | public boolean equals (Object o) { 52 | if (this == o) { 53 | return true; 54 | } 55 | if (o == null || getClass() != o.getClass()) { 56 | return false; 57 | } 58 | 59 | Input input = (Input) o; 60 | 61 | if (sequence != input.sequence) { 62 | return false; 63 | } 64 | if (previousOutput == null) { 65 | if (input.previousOutput != null) { 66 | return false; 67 | } 68 | } 69 | else if (!previousOutput.equals(input.previousOutput)) { 70 | return false; 71 | } 72 | 73 | return scriptSignature.equals(input.scriptSignature); 74 | } 75 | 76 | @Override 77 | public int hashCode () { 78 | int result = previousOutput == null ? 1 : previousOutput.hashCode(); 79 | result = 31 * result + (int) (sequence ^ (sequence >>> 32)); 80 | result = 31 * result + scriptSignature.hashCode(); 81 | return result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/LatestBlock.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Used as a response to the `getLatestBlock` method in the `BlockExplorer` class. 11 | */ 12 | public class LatestBlock extends SimpleBlock { 13 | private long index; 14 | private List transactionIndexes; 15 | 16 | public LatestBlock (long height, String hash, long time, boolean mainChain, long index, List transactionIndexes) { 17 | super(height, hash, time, mainChain); 18 | this.index = index; 19 | this.transactionIndexes = transactionIndexes; 20 | } 21 | 22 | public LatestBlock (JsonObject b) { 23 | this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), true, b.get("block_index").getAsLong(), null); 24 | 25 | transactionIndexes = new ArrayList(); 26 | for (JsonElement idxElem : b.get("txIndexes").getAsJsonArray()) { 27 | transactionIndexes.add(idxElem.getAsLong()); 28 | } 29 | } 30 | 31 | /** 32 | * @return Transaction indexes included in this block 33 | */ 34 | public List getTransactionIndexes () { 35 | return transactionIndexes; 36 | } 37 | 38 | /** 39 | * @return Block index 40 | */ 41 | public long getIndex () { 42 | return index; 43 | } 44 | 45 | @Override 46 | public boolean equals (Object o) { 47 | if (this == o) { 48 | return true; 49 | } 50 | if (o == null || getClass() != o.getClass()) { 51 | return false; 52 | } 53 | if (!super.equals(o)) { 54 | return false; 55 | } 56 | 57 | LatestBlock that = (LatestBlock) o; 58 | 59 | if (index != that.index) { 60 | return false; 61 | } 62 | return transactionIndexes.equals(that.transactionIndexes); 63 | 64 | } 65 | 66 | @Override 67 | public int hashCode () { 68 | int result = (int) (index ^ (index >>> 32)); 69 | result = 31 * result + transactionIndexes.hashCode(); 70 | return result; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddress.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class MultiAddress { 10 | 11 | private MultiAddressBalance multiAddressBalance; 12 | 13 | private List addresses; 14 | 15 | private List txs; 16 | 17 | public MultiAddress (MultiAddressBalance multiAddressBalance, List addresses, List txs) { 18 | this.multiAddressBalance = multiAddressBalance; 19 | this.addresses = addresses; 20 | this.txs = txs; 21 | } 22 | 23 | public MultiAddress (JsonObject json) { 24 | List addresses = new ArrayList(); 25 | for (JsonElement txElem : json.get("addresses").getAsJsonArray()) { 26 | JsonObject addrObj = txElem.getAsJsonObject(); 27 | addresses.add(new AddressSummary(addrObj)); 28 | } 29 | this.addresses = addresses; 30 | 31 | List txs = new ArrayList(); 32 | for (JsonElement txElem : json.get("txs").getAsJsonArray()) { 33 | JsonObject addrObj = txElem.getAsJsonObject(); 34 | txs.add(new Transaction(addrObj)); 35 | } 36 | this.txs = txs; 37 | 38 | this.multiAddressBalance = new MultiAddressBalance(json.getAsJsonObject("wallet")); 39 | } 40 | 41 | public MultiAddressBalance getMultiAddressBalance () { 42 | return multiAddressBalance; 43 | } 44 | 45 | public List getAddresses() { 46 | return addresses; 47 | } 48 | 49 | public List getTxs() { 50 | return txs; 51 | } 52 | 53 | public void setMultiAddressBalance (MultiAddressBalance multiAddressBalance) { 54 | this.multiAddressBalance = multiAddressBalance; 55 | } 56 | 57 | public void setAddresses(List addresses) { 58 | this.addresses = addresses; 59 | } 60 | 61 | public void setTxs(List txs) { 62 | this.txs = txs; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddressBalance.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class MultiAddressBalance { 6 | 7 | private int txCount; 8 | private int txCountFiltered; 9 | private long totalReceived; 10 | private long totalSent; 11 | private long finalBalance; 12 | 13 | public MultiAddressBalance (int txCount, int txCountFiltered, long totalReceived, long totalSent, long finalBalance) { 14 | this.txCount = txCount; 15 | this.txCountFiltered = txCountFiltered; 16 | this.totalReceived = totalReceived; 17 | this.totalSent = totalSent; 18 | this.finalBalance = finalBalance; 19 | } 20 | 21 | public MultiAddressBalance (JsonObject json) { 22 | this( 23 | json.has("n_tx") ? json.get("n_tx").getAsInt() : 0, 24 | json.has("n_tx_filtered") ? json.get("n_tx").getAsInt() : 0, 25 | json.has("total_received") ? json.get("total_received").getAsLong() : 0, 26 | json.has("total_sent") ? json.get("total_sent").getAsLong() : 0, 27 | json.has("final_balance") ? json.get("final_balance").getAsLong() : 0 28 | ); 29 | } 30 | 31 | public int getTxCount() { 32 | return txCount; 33 | } 34 | 35 | public void setTxCount(int txCount) { 36 | this.txCount = txCount; 37 | } 38 | 39 | public long getTotalReceived() { 40 | return totalReceived; 41 | } 42 | 43 | public long getTotalSent() { 44 | return totalSent; 45 | } 46 | 47 | public long getFinalBalance() { 48 | return finalBalance; 49 | } 50 | 51 | public void setFinalBalance(long final_balance) { 52 | this.finalBalance = final_balance; 53 | } 54 | 55 | public void setTotalReceived(long total_received) { 56 | this.totalReceived = total_received; 57 | } 58 | 59 | public void setTotalSent(long total_sent) { 60 | this.totalSent = total_sent; 61 | } 62 | 63 | public int getTxCountFiltered () { 64 | return txCountFiltered; 65 | } 66 | 67 | public void setTxCountFiltered (int txCountFiltered) { 68 | this.txCountFiltered = txCountFiltered; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Output.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | /** 6 | * Represents a transaction output. 7 | */ 8 | public class Output { 9 | private int n; 10 | private long value; 11 | private String address; 12 | private long txIndex; 13 | private String script; 14 | private boolean spent; 15 | private boolean spentToAddress; 16 | 17 | public Output (int n, long value, String address, long txIndex, String script, boolean spent) { 18 | this.n = n; 19 | this.value = value; 20 | this.address = address; 21 | this.txIndex = txIndex; 22 | this.script = script; 23 | this.spent = spent; 24 | if (address != "") { 25 | spentToAddress = true; 26 | } 27 | } 28 | 29 | @Override 30 | public boolean equals (Object o) { 31 | if (this == o) { 32 | return true; 33 | } 34 | if (o == null || getClass() != o.getClass()) { 35 | return false; 36 | } 37 | 38 | Output output = (Output) o; 39 | 40 | if (value != output.value) { 41 | return false; 42 | } 43 | if (txIndex != output.txIndex) { 44 | return false; 45 | } 46 | if (n != output.n) { 47 | return false; 48 | } 49 | 50 | return !(script != null ? !script.equals(output.script) : output.script != null); 51 | 52 | } 53 | 54 | @Override 55 | public int hashCode () { 56 | int result = (int) (value ^ (value >>> 32)); 57 | result = 31 * result + (int) (txIndex ^ (txIndex >>> 32)); 58 | result = 31 * result + (int) (n ^ (n >>> 32)); 59 | result = 31 * result + (script != null ? script.hashCode() : 0); 60 | return result; 61 | } 62 | 63 | public Output (JsonObject o) { 64 | this(o, o.get("spent").getAsBoolean()); 65 | } 66 | 67 | public Output (JsonObject o, boolean spent) { 68 | this(o.get("n").getAsInt(), o.get("value").getAsLong(), o.has("addr") ? o.get("addr").getAsString() : "", o.get("tx_index").getAsLong(), o.get("script").getAsString(), spent); 69 | } 70 | 71 | /** 72 | * @return Index of the output in a transaction 73 | */ 74 | public int getN () { 75 | return n; 76 | } 77 | 78 | /** 79 | * @return Value of the output (in satoshi) 80 | */ 81 | public long getValue () { 82 | return value; 83 | } 84 | 85 | /** 86 | * @return Address that the output belongs to 87 | */ 88 | public String getAddress () { 89 | return address; 90 | } 91 | 92 | /** 93 | * @return Transaction index 94 | */ 95 | public long getTxIndex () { 96 | return txIndex; 97 | } 98 | 99 | /** 100 | * @return Output script 101 | */ 102 | public String getScript () { 103 | return script; 104 | } 105 | 106 | /** 107 | * @return Whether the output is spent 108 | */ 109 | public boolean isSpent () { 110 | return spent; 111 | } 112 | 113 | /** 114 | * @return Whether the output pays to an address. 115 | */ 116 | public boolean isSpentToAddress () { 117 | return spentToAddress; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/SimpleBlock.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | /** 6 | * Simple representation of a block 7 | */ 8 | public class SimpleBlock { 9 | private long height; 10 | private String hash; 11 | private long time; 12 | private boolean mainChain; 13 | 14 | public SimpleBlock (long height, String hash, long time, boolean mainChain) { 15 | this.height = height; 16 | this.hash = hash; 17 | this.time = time; 18 | this.mainChain = mainChain; 19 | } 20 | 21 | public SimpleBlock (JsonObject b) { 22 | this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), b.get("main_chain").getAsBoolean()); 23 | } 24 | 25 | @Override 26 | public boolean equals (Object o) { 27 | if (this == o) { 28 | return true; 29 | } 30 | if (o == null || getClass() != o.getClass()) { 31 | return false; 32 | } 33 | 34 | SimpleBlock that = (SimpleBlock) o; 35 | 36 | if (height != that.height) { 37 | return false; 38 | } 39 | if (time != that.time) { 40 | return false; 41 | } 42 | if (mainChain != that.mainChain) { 43 | return false; 44 | } 45 | return !(hash != null ? !hash.equals(that.hash) : that.hash != null); 46 | 47 | } 48 | 49 | @Override 50 | public int hashCode () { 51 | int result = (int) (height ^ (height >>> 32)); 52 | result = 31 * result + (hash != null ? hash.hashCode() : 0); 53 | result = 31 * result + (int) (time ^ (time >>> 32)); 54 | result = 31 * result + (mainChain ? 1 : 0); 55 | return result; 56 | } 57 | 58 | /** 59 | * @return Block height 60 | */ 61 | public long getHeight () { 62 | return height; 63 | } 64 | 65 | /** 66 | * @return Block hash 67 | */ 68 | public String getHash () { 69 | return hash; 70 | } 71 | 72 | /** 73 | * @return Block timestamp set by the miner (unix time in seconds) 74 | */ 75 | public long getTime () { 76 | return time; 77 | } 78 | 79 | /** 80 | * @return Whether the block is on the main chain 81 | */ 82 | public boolean isMainChain () { 83 | return mainChain; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/Transaction.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Represents a transaction. 11 | */ 12 | public class Transaction { 13 | private boolean doubleSpend; 14 | private long blockHeight; 15 | private long time; 16 | private long lockTime; 17 | private String relayedBy; 18 | private String hash; 19 | private long index; 20 | private int version; 21 | private long size; 22 | private List inputs; 23 | private List outputs; 24 | 25 | public Transaction (boolean doubleSpend, long blockHeight, long time, long lockTime, String relayedBy, 26 | String hash, long index, int version, long size, List inputs, List outputs) { 27 | this.doubleSpend = doubleSpend; 28 | this.blockHeight = blockHeight; 29 | this.time = time; 30 | this.lockTime = lockTime; 31 | this.relayedBy = relayedBy; 32 | this.hash = hash; 33 | this.index = index; 34 | this.version = version; 35 | this.size = size; 36 | this.inputs = inputs; 37 | this.outputs = outputs; 38 | } 39 | 40 | public Transaction (JsonObject t) { 41 | this(t, t.has("block_height") ? t.get("block_height").getAsLong() : -1, 42 | t.has("double_spend") ? t.get("double_spend").getAsBoolean() : false); 43 | } 44 | 45 | public Transaction (JsonObject t, long blockHeight, boolean doubleSpend) { 46 | this(doubleSpend, blockHeight, t.get("time").getAsLong(), t.get("lock_time").getAsLong(), 47 | t.get("relayed_by").getAsString(), t.get("hash").getAsString(), t.get("tx_index").getAsLong(), 48 | t.get("ver").getAsInt(), t.get("size").getAsLong(), null, null); 49 | 50 | inputs = new ArrayList(); 51 | for (JsonElement inputElem : t.get("inputs").getAsJsonArray()) { 52 | inputs.add(new Input(inputElem.getAsJsonObject())); 53 | } 54 | 55 | outputs = new ArrayList(); 56 | for (JsonElement outputElem : t.get("out").getAsJsonArray()) { 57 | outputs.add(new Output(outputElem.getAsJsonObject())); 58 | } 59 | } 60 | 61 | @Override 62 | public boolean equals (Object o) { 63 | if (this == o) { 64 | return true; 65 | } 66 | if (o == null || getClass() != o.getClass()) { 67 | return false; 68 | } 69 | 70 | Transaction that = (Transaction) o; 71 | 72 | if (index != that.index) { 73 | return false; 74 | } 75 | if (version != that.version) { 76 | return false; 77 | } 78 | if (size != that.size) { 79 | return false; 80 | } 81 | if (hash != null ? !hash.equals(that.hash) : that.hash != null) { 82 | return false; 83 | } 84 | if (inputs != null ? !inputs.equals(that.inputs) : that.inputs != null) { 85 | return false; 86 | } 87 | return !(outputs != null ? !outputs.equals(that.outputs) : that.outputs != null); 88 | 89 | } 90 | 91 | @Override 92 | public int hashCode () { 93 | int result = hash != null ? hash.hashCode() : 0; 94 | result = 31 * result + (int) (index ^ (index >>> 32)); 95 | result = 31 * result + version; 96 | result = 31 * result + (int) (size ^ (size >>> 32)); 97 | result = 31 * result + (inputs != null ? inputs.hashCode() : 0); 98 | result = 31 * result + (outputs != null ? outputs.hashCode() : 0); 99 | return result; 100 | } 101 | 102 | /** 103 | * @return Whether the transaction is a double spend 104 | */ 105 | public boolean isDoubleSpend () { 106 | return doubleSpend; 107 | } 108 | 109 | /** 110 | * @return Block height of the parent block. -1 for unconfirmed transactions. 111 | */ 112 | public long getBlockHeight () { 113 | return blockHeight; 114 | } 115 | 116 | /** 117 | * @return Timestamp of the transaction 118 | */ 119 | public long getTime () { 120 | return time; 121 | } 122 | 123 | /** 124 | * @return Locktime of the transaction 125 | */ 126 | public long getLockTime () { 127 | return lockTime; 128 | } 129 | 130 | /** 131 | * @return IP address that relayed the transaction 132 | */ 133 | public String getRelayedBy () { 134 | return relayedBy; 135 | } 136 | 137 | /** 138 | * @return Transaction hash 139 | */ 140 | public String getHash () { 141 | return hash; 142 | } 143 | 144 | /** 145 | * @return Transaction index 146 | */ 147 | public long getIndex () { 148 | return index; 149 | } 150 | 151 | /** 152 | * @return Transaction format version 153 | */ 154 | public int getVersion () { 155 | return version; 156 | } 157 | 158 | /** 159 | * @return Serialized size of the transaction 160 | */ 161 | public long getSize () { 162 | return size; 163 | } 164 | 165 | /** 166 | * @return List of inputs 167 | */ 168 | public List getInputs () { 169 | return inputs; 170 | } 171 | 172 | /** 173 | * @return List of outputs 174 | */ 175 | public List getOutputs () { 176 | return outputs; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/UnspentOutput.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | /** 6 | * Represents an unspent transaction output. 7 | */ 8 | public class UnspentOutput { 9 | private int n; 10 | private String transactionHash; 11 | private long transactionIndex; 12 | private String script; 13 | private long value; 14 | private long confirmations; 15 | 16 | public UnspentOutput (int n, String transactionHash, long transactionIndex, String script, long value, long confirmations) { 17 | this.n = n; 18 | this.transactionHash = transactionHash; 19 | this.transactionIndex = transactionIndex; 20 | this.script = script; 21 | this.value = value; 22 | this.confirmations = confirmations; 23 | } 24 | 25 | public UnspentOutput (JsonObject o) { 26 | this(o.get("tx_output_n").getAsInt(), o.get("tx_hash").getAsString(), o.get("tx_index").getAsLong(), o.get("script").getAsString(), o.get("value").getAsLong(), o.get("confirmations").getAsLong()); 27 | } 28 | 29 | @Override 30 | public boolean equals (Object o) { 31 | if (this == o) { 32 | return true; 33 | } 34 | if (o == null || getClass() != o.getClass()) { 35 | return false; 36 | } 37 | 38 | UnspentOutput that = (UnspentOutput) o; 39 | 40 | if (transactionIndex != that.transactionIndex) { 41 | return false; 42 | } 43 | if (value != that.value) { 44 | return false; 45 | } 46 | if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) { 47 | return false; 48 | } 49 | return !(script != null ? !script.equals(that.script) : that.script != null); 50 | 51 | } 52 | 53 | @Override 54 | public int hashCode () { 55 | int result = transactionHash != null ? transactionHash.hashCode() : 0; 56 | result = 31 * result + (int) (transactionIndex ^ (transactionIndex >>> 32)); 57 | result = 31 * result + (script != null ? script.hashCode() : 0); 58 | result = 31 * result + (int) (value ^ (value >>> 32)); 59 | return result; 60 | } 61 | 62 | /** 63 | * @return Index of the output in a transaction 64 | */ 65 | public int getN () { 66 | return n; 67 | } 68 | 69 | /** 70 | * @return Transaction hash 71 | */ 72 | public String getTransactionHash () { 73 | return transactionHash; 74 | } 75 | 76 | /** 77 | * @return Transaction index 78 | */ 79 | public long getTransactionIndex () { 80 | return transactionIndex; 81 | } 82 | 83 | /** 84 | * @return Output script 85 | */ 86 | public String getScript () { 87 | return script; 88 | } 89 | 90 | /** 91 | * @return Value of the output (in satoshi) 92 | */ 93 | public long getValue () { 94 | return value; 95 | } 96 | 97 | /** 98 | * @return Number of confirmations 99 | */ 100 | public long getConfirmations () { 101 | return confirmations; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/blockexplorer/entity/XpubFull.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer.entity; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class XpubFull { 10 | 11 | private String address; 12 | private int txCount; 13 | private long totalReceived; 14 | private long totalSent; 15 | private long finalBalance; 16 | private int changeIndex; 17 | private int accountIndex; 18 | private int gapLimit; 19 | private List txs; 20 | 21 | public XpubFull (String address, int txCount, long totalReceived, long totalSent, long finalBalance, int changeIndex, int accountIndex, int gapLimit, 22 | List txs) { 23 | this.address = address; 24 | this.txCount = txCount; 25 | this.totalReceived = totalReceived; 26 | this.totalSent = totalSent; 27 | this.finalBalance = finalBalance; 28 | this.changeIndex = changeIndex; 29 | this.accountIndex = accountIndex; 30 | this.gapLimit = gapLimit; 31 | this.txs = txs; 32 | } 33 | 34 | public XpubFull (AddressSummary addressSummary, List txs) { 35 | this.address = addressSummary.getAddress(); 36 | this.txCount = addressSummary.getTxCount(); 37 | this.accountIndex = addressSummary.getAccountIndex(); 38 | this.changeIndex = addressSummary.getChangeIndex(); 39 | this.finalBalance = addressSummary.getFinalBalance(); 40 | this.gapLimit = addressSummary.getGapLimit(); 41 | this.totalReceived = addressSummary.getTotalReceived(); 42 | this.totalSent = addressSummary.getTotalSent(); 43 | this.txs = txs; 44 | } 45 | 46 | public XpubFull (JsonObject json) { 47 | this( 48 | json.has("address") ? json.get("address").getAsString() : "", 49 | json.has("n_tx") ? json.get("n_tx").getAsInt() : 0, 50 | json.has("total_received") ? json.get("total_received").getAsLong() : 0, 51 | json.has("total_sent") ? json.get("total_sent").getAsLong() : 0, 52 | json.has("final_balance") ? json.get("final_balance").getAsLong() : 0, 53 | json.has("change_index") ? json.get("change_index").getAsInt() : 0, 54 | json.has("account_index") ? json.get("account_index").getAsInt() : 0, 55 | json.has("gap_limit") ? json.get("gap_limit").getAsInt() : 0, 56 | null 57 | ); 58 | 59 | txs = new ArrayList(); 60 | for (JsonElement txElem : json.get("txs").getAsJsonArray()) { 61 | JsonObject addrObj = txElem.getAsJsonObject(); 62 | txs.add(new Transaction(addrObj)); 63 | } 64 | } 65 | 66 | public String getAddress() { 67 | return address; 68 | } 69 | 70 | public int getTxCount() { 71 | return txCount; 72 | } 73 | 74 | public void setTxCount(int txCount) { 75 | this.txCount = txCount; 76 | } 77 | 78 | public long getTotalReceived() { 79 | return totalReceived; 80 | } 81 | 82 | public long getTotalSent() { 83 | return totalSent; 84 | } 85 | 86 | public long getFinalBalance() { 87 | return finalBalance; 88 | } 89 | 90 | public int getChangeIndex() { 91 | return changeIndex; 92 | } 93 | 94 | public int getAccountIndex() { 95 | return accountIndex; 96 | } 97 | 98 | public void setFinalBalance(long final_balance) { 99 | this.finalBalance = final_balance; 100 | } 101 | 102 | public void setAddress(String address) { 103 | this.address = address; 104 | } 105 | 106 | public void setTotalReceived(long total_received) { 107 | this.totalReceived = total_received; 108 | } 109 | 110 | public void setTotalSent(long total_sent) { 111 | this.totalSent = total_sent; 112 | } 113 | 114 | public void setChangeIndex(int change_index) { 115 | this.changeIndex = change_index; 116 | } 117 | 118 | public void setAccountIndex(int account_index) { 119 | this.accountIndex = account_index; 120 | } 121 | 122 | public int getGapLimit () { 123 | return gapLimit; 124 | } 125 | 126 | public void setGapLimit (int gapLimit) { 127 | this.gapLimit = gapLimit; 128 | } 129 | 130 | public List getTxs () { 131 | return txs; 132 | } 133 | 134 | public void setTxs (List txs) { 135 | this.txs = txs; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/etc/Base58.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.etc; 2 | 3 | /** 4 | * Copyright 2011 Google Inc. 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 | * http://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 | import java.math.BigInteger; 20 | import java.security.MessageDigest; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.text.ParseException; 23 | import java.util.Arrays; 24 | 25 | /** 26 | * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings. 27 | *

28 | * Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet. 29 | *

30 | * Satoshi explains: why base-58 instead of standard base-64 encoding? 31 | *

    32 | *
  • Don't want 0OIl characters that look the same in some fonts and 33 | * could be used to create visually identical looking account numbers.
  • 34 | *
  • A string with non-alphanumeric characters is not as easily accepted as an account number.
  • 35 | *
  • E-mail usually won't line-break if there's no punctuation to break at.
  • 36 | *
  • Doubleclicking selects the whole number as one word if it's all alphanumeric.
  • 37 | *
38 | *

39 | * However, note that the encoding/decoding runs in O(n²) time, so it is not useful for large data. 40 | *

41 | * The basic idea of the encoding is to treat the data bytes as a large number represented using 42 | * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact 43 | * number of leading zeros (which are otherwise lost during the mathematical operations on the 44 | * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters. 45 | */ 46 | public class Base58 { 47 | public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); 48 | private static final char ENCODED_ZERO = ALPHABET[0]; 49 | private static final int[] INDEXES = new int[128]; 50 | 51 | static { 52 | Arrays.fill(INDEXES, -1); 53 | for (int i = 0; i < ALPHABET.length; i++) { 54 | INDEXES[ALPHABET[i]] = i; 55 | } 56 | } 57 | 58 | /** 59 | * Encodes the given bytes as a base58 string (no checksum is appended). 60 | * 61 | * @param input the bytes to encode 62 | * @return the base58-encoded string 63 | */ 64 | public static String encode (byte[] input) { 65 | if (input.length == 0) { 66 | return ""; 67 | } 68 | // Count leading zeros. 69 | int zeros = 0; 70 | while (zeros < input.length && input[zeros] == 0) { 71 | ++zeros; 72 | } 73 | // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) 74 | input = Arrays.copyOf(input, input.length); // since we modify it in-place 75 | char[] encoded = new char[input.length * 2]; // upper bound 76 | int outputStart = encoded.length; 77 | for (int inputStart = zeros; inputStart < input.length; ) { 78 | encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; 79 | if (input[inputStart] == 0) { 80 | ++inputStart; // optimization - skip leading zeros 81 | } 82 | } 83 | // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input. 84 | while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { 85 | ++outputStart; 86 | } 87 | while (--zeros >= 0) { 88 | encoded[--outputStart] = ENCODED_ZERO; 89 | } 90 | // Return encoded string (including encoded leading zeros). 91 | return new String(encoded, outputStart, encoded.length - outputStart); 92 | } 93 | 94 | /** 95 | * Decodes the given base58 string into the original data bytes. 96 | * 97 | * @param input the base58-encoded string to decode 98 | * @return the decoded data bytes 99 | * @throws ParseException if the given string is not a valid base58 string 100 | */ 101 | public static byte[] decode (String input) throws ParseException { 102 | if (input.length() == 0) { 103 | return new byte[0]; 104 | } 105 | // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). 106 | byte[] input58 = new byte[input.length()]; 107 | for (int i = 0; i < input.length(); ++i) { 108 | char c = input.charAt(i); 109 | int digit = c < 128 ? INDEXES[c] : -1; 110 | if (digit < 0) { 111 | throw new ParseException("Illegal character " + c + " at position " + i, i); 112 | } 113 | input58[i] = (byte) digit; 114 | } 115 | // Count leading zeros. 116 | int zeros = 0; 117 | while (zeros < input58.length && input58[zeros] == 0) { 118 | ++zeros; 119 | } 120 | // Convert base-58 digits to base-256 digits. 121 | byte[] decoded = new byte[input.length()]; 122 | int outputStart = decoded.length; 123 | for (int inputStart = zeros; inputStart < input58.length; ) { 124 | decoded[--outputStart] = divmod(input58, inputStart, 58, 256); 125 | if (input58[inputStart] == 0) { 126 | ++inputStart; // optimization - skip leading zeros 127 | } 128 | } 129 | // Ignore extra leading zeroes that were added during the calculation. 130 | while (outputStart < decoded.length && decoded[outputStart] == 0) { 131 | ++outputStart; 132 | } 133 | // Return decoded data (including original number of leading zeros). 134 | return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); 135 | } 136 | 137 | public static BigInteger decodeToBigInteger (String input) throws ParseException { 138 | return new BigInteger(1, decode(input)); 139 | } 140 | 141 | /** 142 | * Decodes the given base58 string into the original data bytes, using the checksum in the 143 | * last 4 bytes of the decoded data to verify that the rest are correct. The checksum is 144 | * removed from the returned data. 145 | * 146 | * @param input the base58-encoded string to decode (which should include the checksum) 147 | * @throws ParseException if the input is not base 58 or the checksum does not validate. 148 | */ 149 | public static byte[] decodeChecked (String input) throws ParseException { 150 | byte[] decoded = decode(input); 151 | if (decoded.length < 4) { 152 | throw new ParseException("Input too short", 0); 153 | } 154 | byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4); 155 | byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length); 156 | byte[] actualChecksum = Arrays.copyOfRange(hashTwice(data), 0, 4); 157 | if (!Arrays.equals(checksum, actualChecksum)) { 158 | throw new ParseException("Checksum does not validate", 0); 159 | } 160 | return data; 161 | } 162 | 163 | /** 164 | * Divides a number, represented as an array of bytes each containing a single digit 165 | * in the specified base, by the given divisor. The given number is modified in-place 166 | * to contain the quotient, and the return value is the remainder. 167 | * 168 | * @param number the number to divide 169 | * @param firstDigit the index within the array of the first non-zero digit 170 | * (this is used for optimization by skipping the leading zeros) 171 | * @param base the base in which the number's digits are represented (up to 256) 172 | * @param divisor the number to divide by (up to 256) 173 | * @return the remainder of the division operation 174 | */ 175 | private static byte divmod (byte[] number, int firstDigit, int base, int divisor) { 176 | // this is just long division which accounts for the base of the input digits 177 | int remainder = 0; 178 | for (int i = firstDigit; i < number.length; i++) { 179 | int digit = (int) number[i] & 0xFF; 180 | int temp = remainder * base + digit; 181 | number[i] = (byte) (temp / divisor); 182 | remainder = temp % divisor; 183 | } 184 | return (byte) remainder; 185 | } 186 | 187 | public static byte[] hashTwice (byte[] input) { 188 | MessageDigest digest = null; 189 | try { 190 | digest = MessageDigest.getInstance("SHA-256"); 191 | digest.update(input, 0, input.length); 192 | return digest.digest(digest.digest()); 193 | } catch (NoSuchAlgorithmException e) { 194 | throw new RuntimeException(e); 195 | } 196 | 197 | } 198 | } -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/exchangerates/Currency.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.exchangerates; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Used in response to the `getTicker` method in the `ExchangeRates` class. 7 | */ 8 | public class Currency { 9 | private BigDecimal buy; 10 | private BigDecimal sell; 11 | private BigDecimal last; 12 | private BigDecimal price15m; 13 | private String symbol; 14 | 15 | public Currency (double buy, double sell, double last, double price15m, String symbol) { 16 | this.buy = BigDecimal.valueOf(buy); 17 | this.sell = BigDecimal.valueOf(sell); 18 | this.last = BigDecimal.valueOf(last); 19 | this.price15m = BigDecimal.valueOf(price15m); 20 | this.symbol = symbol; 21 | } 22 | 23 | /** 24 | * @return Current buy price 25 | */ 26 | public BigDecimal getBuy () { 27 | return buy; 28 | } 29 | 30 | /** 31 | * @return Current sell price 32 | */ 33 | public BigDecimal getSell () { 34 | return sell; 35 | } 36 | 37 | /** 38 | * @return Most recent market price 39 | */ 40 | public BigDecimal getLast () { 41 | return last; 42 | } 43 | 44 | /** 45 | * @return 15 minutes delayed market price 46 | */ 47 | public BigDecimal getPrice15m () { 48 | return price15m; 49 | } 50 | 51 | /** 52 | * @return Currency symbol 53 | */ 54 | public String getSymbol () { 55 | return symbol; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.exchangerates; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import info.blockchain.api.APIException; 7 | import info.blockchain.api.HttpClient; 8 | 9 | import java.io.IOException; 10 | import java.math.BigDecimal; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Map.Entry; 14 | 15 | /** 16 | * This class reflects the functionality documented 17 | * at https://blockchain.info/api/exchange_rates_api. It allows users to fetch the latest 18 | * ticker data and convert amounts between BTC and fiat currencies. 19 | */ 20 | public class ExchangeRates { 21 | 22 | private final String apiCode; 23 | 24 | public ExchangeRates () { 25 | this(null); 26 | } 27 | 28 | public ExchangeRates (String apiCode) { 29 | this.apiCode = apiCode; 30 | } 31 | 32 | /** 33 | * Gets the price ticker from https://blockchain.info/ticker 34 | * 35 | * @return A map of currencies where the key is a 3-letter currency symbol and the 36 | * value is the `Currency` class 37 | * @throws APIException If the server returns an error 38 | */ 39 | public Map getTicker () throws APIException, IOException { 40 | Map params = new HashMap(); 41 | if (apiCode != null) { 42 | params.put("api_code", apiCode); 43 | } 44 | 45 | String response = HttpClient.getInstance().get("ticker", params); 46 | JsonObject ticker = new JsonParser().parse(response).getAsJsonObject(); 47 | 48 | Map resultMap = new HashMap(); 49 | for (Entry ccyKVP : ticker.entrySet()) { 50 | JsonObject ccy = ccyKVP.getValue().getAsJsonObject(); 51 | Currency currency = new Currency(ccy.get("buy").getAsDouble(), ccy.get("sell").getAsDouble(), ccy.get("last").getAsDouble(), ccy.get("15m").getAsDouble(), ccy.get("symbol").getAsString()); 52 | 53 | resultMap.put(ccyKVP.getKey(), currency); 54 | } 55 | 56 | return resultMap; 57 | } 58 | 59 | /** 60 | * Converts x value in the provided currency to BTC. 61 | * 62 | * @param currency Currency code 63 | * @param value Value to convert 64 | * @return Converted value in BTC 65 | * @throws APIException If the server returns an error 66 | */ 67 | public BigDecimal toBTC (String currency, BigDecimal value) throws APIException, IOException { 68 | Map params = new HashMap(); 69 | params.put("currency", currency); 70 | params.put("value", String.valueOf(value)); 71 | if (apiCode != null) { 72 | params.put("api_code", apiCode); 73 | } 74 | 75 | String response = HttpClient.getInstance().get("tobtc", params); 76 | return new BigDecimal(response); 77 | } 78 | 79 | /** 80 | * Converts x value in BTC to the provided currency. 81 | * 82 | * @param currency Currency code 83 | * @param value Value to convert 84 | * @return Converted value in the provided currency 85 | * @throws APIException If the server returns an error 86 | */ 87 | public BigDecimal toFiat (String currency, BigDecimal value) throws APIException, IOException { 88 | Map params = new HashMap(); 89 | params.put("currency", currency); 90 | params.put("value", String.valueOf(value.multiply(BigDecimal.valueOf(100000000L)))); // The endpoint is expecting satoshi 91 | if (apiCode != null) { 92 | params.put("api_code", apiCode); 93 | } 94 | 95 | String response = HttpClient.getInstance().get("frombtc", params); 96 | return new BigDecimal(response); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/pushtx/PushTx.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.pushtx; 2 | 3 | import info.blockchain.api.APIException; 4 | import info.blockchain.api.HttpClient; 5 | 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * This class reflects the functionality provided at 12 | * https://blockchain.info/pushtx. It allows users to broadcast hex encoded 13 | * transactions to the bitcoin network. 14 | */ 15 | public class PushTx { 16 | /** 17 | * Pushes a hex encoded transaction to the network. 18 | * 19 | * @param tx Hex encoded transaction 20 | * @throws APIException If the server returns an error (malformed tx etc.) 21 | */ 22 | public static void pushTx (String tx) throws APIException, IOException { 23 | pushTx(tx, null); 24 | } 25 | 26 | /** 27 | * Pushes a hex encoded transaction to the network. 28 | * 29 | * @param tx Hex encoded transaction 30 | * @param apiCode Blockchain.info API code (optional, nullable) 31 | * @throws APIException If the server returns an error (malformed tx etc.) 32 | */ 33 | public static void pushTx (String tx, String apiCode) throws APIException, IOException { 34 | Map params = new HashMap(); 35 | params.put("tx", tx); 36 | 37 | if (apiCode != null) { 38 | params.put("api_code", apiCode); 39 | } 40 | 41 | HttpClient.getInstance().post("pushtx", params); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/receive/CallbackLog.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.receive; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class CallbackLog { 6 | private String callback; 7 | private String callbackTime; 8 | private String response; 9 | private int responseCode; 10 | 11 | public CallbackLog (JsonObject callbackJson) { 12 | this( 13 | callbackJson.has("callback") ? callbackJson.get("callback").getAsString() : "", 14 | callbackJson.has("called_at") ? callbackJson.get("called_at").getAsString() : "", 15 | callbackJson.has("raw_response") ? callbackJson.get("raw_response").getAsString() : "", 16 | callbackJson.has("response_code") ? callbackJson.get("response_code").getAsInt() : 0 17 | ); 18 | } 19 | 20 | public CallbackLog (String callback, String callbackTime, String response, int responseCode) { 21 | this.callback = callback; 22 | this.callbackTime = callbackTime; 23 | this.response = response; 24 | this.responseCode = responseCode; 25 | } 26 | 27 | public String getCallback () { 28 | return callback; 29 | } 30 | 31 | public void setCallback (String callback) { 32 | this.callback = callback; 33 | } 34 | 35 | public String getCallbackTime () { 36 | return callbackTime; 37 | } 38 | 39 | public void setCallbackTime (String callbackTime) { 40 | this.callbackTime = callbackTime; 41 | } 42 | 43 | public String getResponse () { 44 | return response; 45 | } 46 | 47 | public void setResponse (String response) { 48 | this.response = response; 49 | } 50 | 51 | public int getResponseCode () { 52 | return responseCode; 53 | } 54 | 55 | public void setResponseCode (int responseCode) { 56 | this.responseCode = responseCode; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/receive/Receive.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.receive; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import info.blockchain.api.APIException; 6 | import info.blockchain.api.HttpClient; 7 | 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | /** 13 | * This class reflects the functionality necessary for using the receive-payments-api v2. 14 | * Passing on a xPUB, callbackUrl and the apiCode will return an address for receiving a payment. 15 | *

16 | * Upon receiving a payment on this address, the merchant will be notified using the callback URL. 17 | */ 18 | public class Receive { 19 | private final String apiCode; 20 | 21 | /** 22 | * @param apiCode Blockchain.info API code for the receive-payments v2 API (different from normal API key) 23 | */ 24 | public Receive (String apiCode) { 25 | this.apiCode = apiCode; 26 | } 27 | 28 | /** 29 | * Calls the receive-payments-api v2 and returns an address for the payment. 30 | * 31 | * @param xPUB Destination address where the payment should be sent 32 | * @param callbackUrl Callback URI that will be called upon payment 33 | * @return An instance of the ReceiveV2Response class 34 | * @throws APIException If the server returns an error 35 | */ 36 | public ReceiveResponse receive (String xPUB, String callbackUrl) throws APIException, IOException { 37 | Map params = new HashMap(); 38 | params.put("xpub", xPUB); 39 | params.put("callback", callbackUrl); 40 | params.put("key", apiCode); 41 | 42 | String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive", params); 43 | JsonParser parser = new JsonParser(); 44 | JsonObject obj = parser.parse(response).getAsJsonObject(); 45 | 46 | return new ReceiveResponse(obj.get("index").getAsInt(), obj.get("address").getAsString(), obj.get("callback").getAsString()); 47 | } 48 | 49 | /** 50 | * Calls the receive-payments-api v2 and returns the xpub gap of an xpub. 51 | * 52 | * @param xPUB Destination address where the payment should be sent 53 | * @return An instance of the ReceiveV2Response class 54 | * @throws APIException If the server returns an error 55 | */ 56 | public int checkGap (String xPUB) throws APIException, IOException { 57 | Map params = new HashMap(); 58 | params.put("xpub", xPUB); 59 | params.put("key", apiCode); 60 | 61 | String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/checkgap", params); 62 | JsonObject obj = new JsonParser().parse(response).getAsJsonObject(); 63 | 64 | return obj.get("gap").getAsInt(); 65 | } 66 | 67 | /** 68 | * Calls the receive-payments-api v2 and returns the callback log based on url. 69 | * 70 | * @param callbackUrl Callback URI that will be called upon payment 71 | * @return An instance of the ReceiveV2Response class 72 | * @throws APIException If the server returns an error 73 | */ 74 | public CallbackLog getCallbackLog (String callbackUrl) throws APIException, IOException { 75 | Map params = new HashMap(); 76 | params.put("callback", callbackUrl); 77 | params.put("key", apiCode); 78 | 79 | String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/callback", params); 80 | JsonObject obj = new JsonParser().parse(response).getAsJsonObject(); 81 | 82 | return new CallbackLog(obj); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/receive/ReceiveResponse.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.receive; 2 | 3 | /** 4 | * This class is used as a response object to the `ReceiveV2.receive` method. 5 | */ 6 | public class ReceiveResponse { 7 | private int index; 8 | private String receivingAddress; 9 | private String callbackUrl; 10 | 11 | public ReceiveResponse (int index, String receivingAddress, String callbackUrl) { 12 | this.index = index; 13 | this.receivingAddress = receivingAddress; 14 | this.callbackUrl = callbackUrl; 15 | } 16 | 17 | /** 18 | * @return Index of the address in the account 19 | */ 20 | public int getIndex () { 21 | return index; 22 | } 23 | 24 | /** 25 | * @return Address to be displayed for the customer at checkout. 26 | */ 27 | public String getReceivingAddress () { 28 | return receivingAddress; 29 | } 30 | 31 | /** 32 | * @return Callback URI that will be called upon payment 33 | */ 34 | public String getCallbackUrl () { 35 | return callbackUrl; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/statistics/Chart.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.statistics; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class Chart { 10 | 11 | private String status; 12 | private String name; 13 | private String unit; 14 | private String period; 15 | private String description; 16 | private List values; 17 | 18 | public Chart (JsonObject chartJson) { 19 | this( 20 | chartJson.has("status") ? chartJson.get("status").getAsString() : "", 21 | chartJson.has("name") ? chartJson.get("name").getAsString() : "", 22 | chartJson.has("unit") ? chartJson.get("unit").getAsString() : "", 23 | chartJson.has("period") ? chartJson.get("period").getAsString() : "", 24 | chartJson.has("description") ? chartJson.get("description").getAsString() : "", 25 | null 26 | ); 27 | values = getPoints(chartJson); 28 | } 29 | 30 | public Chart (String status, String name, String unit, String period, String description, List values) { 31 | this.status = status; 32 | this.name = name; 33 | this.unit = unit; 34 | this.period = period; 35 | this.description = description; 36 | this.values = values; 37 | } 38 | 39 | private List getPoints(JsonObject chartJson) { 40 | List points = new ArrayList(); 41 | for (JsonElement pointElement : chartJson.getAsJsonArray()) { 42 | JsonObject pointJson = pointElement.getAsJsonObject(); 43 | points.add(new Point(pointJson)); 44 | } 45 | 46 | return points; 47 | } 48 | 49 | public String getStatus() { 50 | return status; 51 | } 52 | 53 | public String getName() { 54 | return name; 55 | } 56 | 57 | public String getUnit() { 58 | return unit; 59 | } 60 | 61 | public String getPeriod() { 62 | return period; 63 | } 64 | 65 | public String getDescription() { 66 | return description; 67 | } 68 | 69 | public List getValues() { 70 | return values; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/statistics/Point.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.statistics; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class Point { 6 | 7 | private float x; 8 | private float y; 9 | 10 | public Point (JsonObject pointJson) { 11 | this( 12 | pointJson.has("x") ? pointJson.get("x").getAsFloat() : 0.0F, 13 | pointJson.has("y") ? pointJson.get("y").getAsFloat() : 0.0F 14 | ); 15 | } 16 | 17 | public Point (float x, float y) { 18 | this.x = x; 19 | this.y = y; 20 | } 21 | 22 | public float getX() { 23 | return x; 24 | } 25 | 26 | public float getY() { 27 | return y; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/statistics/Statistics.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.statistics; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import com.google.gson.reflect.TypeToken; 7 | import info.blockchain.api.APIException; 8 | import info.blockchain.api.HttpClient; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.Type; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * This class reflects the functionality documented 17 | * at https://blockchain.info/api/charts_api 18 | */ 19 | public class Statistics { 20 | private final String apiCode; 21 | 22 | public Statistics () { 23 | this(null); 24 | } 25 | 26 | public Statistics (String apiCode) { 27 | this.apiCode = apiCode; 28 | } 29 | 30 | /** 31 | * Gets the network statistics. 32 | * 33 | * @return An instance of the StatisticsResponse class 34 | * @throws APIException If the server returns an error 35 | */ 36 | public StatisticsResponse getStats () throws APIException, IOException { 37 | Map params = new HashMap(); 38 | params.put("format", "json"); 39 | if (apiCode != null) { 40 | params.put("api_code", apiCode); 41 | } 42 | 43 | String response = HttpClient.getInstance().get("stats", params); 44 | return new StatisticsResponse(response); 45 | } 46 | 47 | /** 48 | * This method can be used to get and manipulate data behind all Blockchain.info's charts. 49 | * 50 | * @param type of chart (Example: transactions-per-second, total-bitcoins) 51 | * @param timeSpan (Example: 5weeks) 52 | * @param rollingAverage (Example: 8hours) 53 | * @return {@code Chart} represents the series of data of the chart 54 | * @throws APIException If the server returns an error 55 | */ 56 | public Chart getChart(String type, String timeSpan, String rollingAverage) throws APIException, IOException { 57 | Map params = new HashMap(); 58 | params.put("format", "json"); 59 | if (apiCode != null) { 60 | params.put("api_code", apiCode); 61 | } 62 | if (timeSpan != null) { 63 | params.put("timespan", timeSpan); 64 | } 65 | if (rollingAverage != null) { 66 | params.put("rollingAverage", rollingAverage); 67 | } 68 | 69 | String response = HttpClient.getInstance().get("charts/" + type, params); 70 | JsonObject chartJson = new JsonParser().parse(response).getAsJsonObject(); 71 | 72 | return new Chart(chartJson); 73 | } 74 | 75 | /** 76 | * This method can be used to get the data behind Blockchain.info's pools information. 77 | * 78 | * @param timeSpan (Example: 5weeks) 79 | * @return a map of pool name and the number of blocks it mined 80 | * @throws APIException If the server returns an error 81 | */ 82 | public Map getPools(String timeSpan) throws APIException, IOException { 83 | Map params = new HashMap(); 84 | params.put("format", "json"); 85 | if (apiCode != null) { 86 | params.put("api_code", apiCode); 87 | } 88 | if (timeSpan != null) { 89 | params.put("timespan", timeSpan); 90 | } 91 | 92 | String response = HttpClient.getInstance().get("pools", params); 93 | Type type = new TypeToken>(){}.getType(); 94 | Gson gson = new Gson(); 95 | Map pools = gson.fromJson(response, type); 96 | 97 | return pools; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/statistics/StatisticsResponse.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.statistics; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | 6 | import java.math.BigDecimal; 7 | 8 | /** 9 | * This class is used as a response object to the 'get' method in the 'Statistics' class 10 | */ 11 | public class StatisticsResponse { 12 | private BigDecimal tradeVolumeBTC; 13 | private BigDecimal tradeVolumeUSD; 14 | private BigDecimal minersRevenueBTC; 15 | private BigDecimal minersRevenueUSD; 16 | private BigDecimal marketPriceUSD; 17 | private BigDecimal estimatedTransactionVolumeUSD; 18 | private long totalFeesBTC; 19 | private long totalBTCSent; 20 | private long estimatedBTCSent; 21 | private long btcMined; 22 | private double difficulty; 23 | private double minutesBetweenBlocks; 24 | private long numberOfTransactions; 25 | private double hashRate; 26 | private long timestamp; 27 | private long minedBlocks; 28 | private long blocksSize; 29 | private long totalBTC; 30 | private long totalBlocks; 31 | private long nextRetarget; 32 | 33 | public StatisticsResponse (String jsonString) { 34 | JsonObject s = new JsonParser().parse(jsonString).getAsJsonObject(); 35 | 36 | this.tradeVolumeBTC = new BigDecimal(s.get("trade_volume_btc").getAsString()); 37 | this.tradeVolumeUSD = new BigDecimal(s.get("trade_volume_usd").getAsString()); 38 | this.minersRevenueBTC = new BigDecimal(s.get("miners_revenue_btc").getAsString()); 39 | this.minersRevenueUSD = new BigDecimal(s.get("miners_revenue_usd").getAsString()); 40 | this.marketPriceUSD = new BigDecimal(s.get("market_price_usd").getAsString()); 41 | this.estimatedTransactionVolumeUSD = new BigDecimal(s.get("estimated_transaction_volume_usd").getAsString()); 42 | this.totalFeesBTC = s.get("total_fees_btc").getAsLong(); 43 | this.totalBTCSent = s.get("total_btc_sent").getAsLong(); 44 | this.estimatedBTCSent = s.get("estimated_btc_sent").getAsLong(); 45 | this.btcMined = s.get("n_btc_mined").getAsLong(); 46 | this.difficulty = s.get("difficulty").getAsDouble(); 47 | this.minutesBetweenBlocks = s.get("minutes_between_blocks").getAsDouble(); 48 | this.numberOfTransactions = s.get("n_tx").getAsLong(); 49 | this.hashRate = s.get("hash_rate").getAsDouble(); 50 | this.timestamp = s.get("timestamp").getAsLong(); 51 | this.minedBlocks = s.get("n_blocks_mined").getAsLong(); 52 | this.blocksSize = s.get("blocks_size").getAsLong(); 53 | this.totalBTC = s.get("totalbc").getAsLong(); 54 | this.totalBlocks = s.get("n_blocks_total").getAsLong(); 55 | this.nextRetarget = s.get("nextretarget").getAsLong(); 56 | } 57 | 58 | /** 59 | * @return Trade volume in the past 24 hours 60 | */ 61 | public BigDecimal getTradeVolumeBTC () { 62 | return tradeVolumeBTC; 63 | } 64 | 65 | /** 66 | * @return Trade volume in the past 24 hours 67 | */ 68 | public BigDecimal getTradeVolumeUSD () { 69 | return tradeVolumeUSD; 70 | } 71 | 72 | /** 73 | * @return Miners' revenue in BTC 74 | */ 75 | public BigDecimal getMinersRevenueBTC () { 76 | return minersRevenueBTC; 77 | } 78 | 79 | /** 80 | * @return Miners' revenue in USD 81 | */ 82 | public BigDecimal getMinersRevenueUSD () { 83 | return minersRevenueUSD; 84 | } 85 | 86 | /** 87 | * @return Current market price in USD 88 | */ 89 | public BigDecimal getMarketPriceUSD () { 90 | return marketPriceUSD; 91 | } 92 | 93 | /** 94 | * @return Estimated transaction volume in the past 24 hours 95 | */ 96 | public BigDecimal getEstimatedTransactionVolumeUSD () { 97 | return estimatedTransactionVolumeUSD; 98 | } 99 | 100 | /** 101 | * @return Total fees in the past 24 hours (in satoshi) 102 | */ 103 | public long getTotalFeesBTC () { 104 | return totalFeesBTC; 105 | } 106 | 107 | /** 108 | * @return Total BTC sent in the past 24 hours (in satoshi) 109 | */ 110 | public long getTotalBTCSent () { 111 | return totalBTCSent; 112 | } 113 | 114 | /** 115 | * @return Estimated BTC sent in the past 24 hours (in satoshi) 116 | */ 117 | public long getEstimatedBTCSent () { 118 | return estimatedBTCSent; 119 | } 120 | 121 | /** 122 | * @return Number of BTC mined in the past 24 hours (in satoshi) 123 | */ 124 | public long getBTCMined () { 125 | return btcMined; 126 | } 127 | 128 | /** 129 | * @return Current difficulty 130 | */ 131 | public double getDifficulty () { 132 | return difficulty; 133 | } 134 | 135 | /** 136 | * @return Minutes between blocks 137 | */ 138 | public double getMinutesBetweenBlocks () { 139 | return minutesBetweenBlocks; 140 | } 141 | 142 | /** 143 | * @return Number of transactions in the past 24 hours 144 | */ 145 | public long getNumberOfTransactions () { 146 | return numberOfTransactions; 147 | } 148 | 149 | /** 150 | * @return Current hashrate in GH/s 151 | */ 152 | public double getHashRate () { 153 | return hashRate; 154 | } 155 | 156 | /** 157 | * @return Timestamp of when this report was compiled (in ms) 158 | */ 159 | public long getTimestamp () { 160 | return timestamp; 161 | } 162 | 163 | /** 164 | * @return Number of blocks mined in the past 24 hours 165 | */ 166 | public long getMinedBlocks () { 167 | return minedBlocks; 168 | } 169 | 170 | /** 171 | * @return the blocksSize 172 | */ 173 | public long getBlocksSize () { 174 | return blocksSize; 175 | } 176 | 177 | /** 178 | * @return Total BTC in existence (in satoshi) 179 | */ 180 | public long getTotalBTC () { 181 | return totalBTC; 182 | } 183 | 184 | /** 185 | * @return Total number of blocks in existence (in satoshi) 186 | */ 187 | public long getTotalBlocks () { 188 | return totalBlocks; 189 | } 190 | 191 | /** 192 | * @return The next block height where the difficulty retarget will occur 193 | */ 194 | public long getNextRetarget () { 195 | return nextRetarget; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/wallet/Wallet.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.wallet; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | 10 | import com.google.gson.Gson; 11 | import com.google.gson.JsonElement; 12 | import com.google.gson.JsonObject; 13 | import com.google.gson.JsonParser; 14 | 15 | import info.blockchain.api.APIException; 16 | import info.blockchain.api.HttpClient; 17 | import info.blockchain.api.wallet.entity.Address; 18 | import info.blockchain.api.wallet.entity.CreateWalletResponse; 19 | import info.blockchain.api.wallet.entity.PaymentResponse; 20 | 21 | /** 22 | * This class reflects the functionality documented 23 | * at https://blockchain.info/api/blockchain_wallet_api. It allows users to interact 24 | * with their Blockchain.info wallet. If you have an API code, please set it via the 25 | * `setApiCode` method. 26 | */ 27 | public class Wallet { 28 | private JsonParser jsonParser; 29 | 30 | private String serviceURL; 31 | private String identifier; 32 | private String password; 33 | private String secondPassword; 34 | private String apiCode; 35 | 36 | /** 37 | * Creates a new Blockchain.info wallet. 38 | * 39 | * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash) 40 | * @param password Password for the new wallet. At least 10 characters. 41 | * @param apiCode API code with create wallets permission 42 | * @return An instance of the CreateWalletResponse class 43 | * @throws APIException If the server returns an error 44 | */ 45 | public static CreateWalletResponse create (String serviceURL, String password, 46 | String apiCode) throws IOException, APIException { 47 | return create(serviceURL, password, apiCode, null, null, null); 48 | } 49 | 50 | /** 51 | * Creates a new Blockchain.info wallet. It can be created containing a pre-generated 52 | * private key or will otherwise generate a new private key. 53 | * 54 | * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash) 55 | * @param password Password for the new wallet. At least 10 characters. 56 | * @param apiCode API code with create wallets permission 57 | * @param privateKey Private key to add to the wallet (optional, nullable) 58 | * @param label Label for the first address in the wallet (optional, nullable) 59 | * @param email Email to associate with the new wallet (optional, nullable) 60 | * @return An instance of the CreateWalletResponse class 61 | * @throws APIException If the server returns an error 62 | */ 63 | public static CreateWalletResponse create (String serviceURL, String password,String apiCode, 64 | String privateKey, String label, String email) throws IOException, APIException { 65 | Map params = new HashMap(); 66 | 67 | params.put("password", password); 68 | params.put("api_code", apiCode); 69 | if (privateKey != null) { 70 | params.put("priv", privateKey); 71 | } 72 | if (label != null) { 73 | params.put("label", label); 74 | } 75 | if (email != null) { 76 | params.put("email", email); 77 | } 78 | 79 | String response = HttpClient.getInstance().post(serviceURL, "api/v2/create", params); 80 | JsonObject jsonObj = new JsonParser().parse(response).getAsJsonObject(); 81 | 82 | return new CreateWalletResponse( 83 | jsonObj.get("guid").getAsString(), 84 | jsonObj.get("address").getAsString(), 85 | jsonObj.get("label").getAsString()); 86 | } 87 | 88 | /** 89 | * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash) 90 | * @param apiCode API Code 91 | * @param identifier Wallet identifier (GUID) 92 | * @param password Decryption password 93 | */ 94 | public Wallet (String serviceURL, String apiCode, String identifier, String password) { 95 | this(serviceURL, apiCode, identifier, password, null); 96 | } 97 | 98 | /** 99 | * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash) 100 | * @param apiCode API Code 101 | * @param identifier Wallet identifier (GUID) 102 | * @param password Decryption password 103 | * @param secondPassword Second password 104 | */ 105 | public Wallet (String serviceURL, String apiCode, String identifier, String password, String secondPassword) { 106 | this.serviceURL = serviceURL; 107 | this.apiCode = apiCode; 108 | this.identifier = identifier; 109 | this.password = password; 110 | this.secondPassword = secondPassword; 111 | this.jsonParser = new JsonParser(); 112 | } 113 | 114 | /** 115 | * Sends bitcoin from your wallet to a single address. 116 | * 117 | * @param toAddress Recipient bitcoin address 118 | * @param amount Amount to send (in satoshi) 119 | * @param fromAddress Specific address to send from (optional, nullable) 120 | * @param fee Transaction fee in satoshi. Must be greater than the default fee (optional, nullable). 121 | * @return An instance of the PaymentResponse class 122 | * @throws APIException If the server returns an error 123 | */ 124 | public PaymentResponse send (String toAddress, long amount, String fromAddress, Long fee) throws APIException, IOException { 125 | Map recipient = new HashMap(); 126 | recipient.put(toAddress, amount); 127 | 128 | return sendMany(recipient, fromAddress, fee); 129 | } 130 | 131 | /** 132 | * Sends bitcoin from your wallet to multiple addresses. 133 | * 134 | * @param recipients Map with the structure of 'address':amount in satoshi (String:long) 135 | * @param fromAddress Specific address to send from (optional, nullable) 136 | * @param fee Transaction fee in satoshi. Must be greater than the default fee (optional, nullable). 137 | * @return An instance of the PaymentResponse class 138 | * @throws APIException If the server returns an error 139 | * @throws IllegalArgumentException If no recipients is null or empty 140 | */ 141 | public PaymentResponse sendMany (Map recipients, String fromAddress, Long fee) throws APIException, IOException { 142 | Map params = buildBasicRequestWithSecondPassword(); 143 | String method = null; 144 | 145 | if (recipients.size() == 1) { 146 | method = "payment"; 147 | Entry e = recipients.entrySet().iterator().next(); 148 | params.put("to", e.getKey()); 149 | params.put("amount", e.getValue().toString()); 150 | } else { 151 | method = "sendmany"; 152 | params.put("recipients", new Gson().toJson(recipients)); 153 | } 154 | 155 | if (fromAddress != null) { 156 | params.put("from", fromAddress); 157 | } 158 | if (fee != null) { 159 | params.put("fee", fee.toString()); 160 | } 161 | 162 | String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/%s", identifier, method), params); 163 | JsonObject topElem = parseResponse(response); 164 | 165 | return new PaymentResponse(topElem.get("message").getAsString(), topElem.get("tx_hash").getAsString(), topElem.has("notice") ? topElem.get("notice").getAsString() : null); 166 | } 167 | 168 | /** 169 | * Fetches the wallet balance. Includes unconfirmed transactions and 170 | * possibly double spends. 171 | * 172 | * @return Wallet balance in satoshi 173 | * @throws APIException If the server returns an error 174 | */ 175 | public long getBalance () throws APIException, IOException { 176 | String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/balance", identifier), buildBasicRequest()); 177 | JsonObject topElem = parseResponse(response); 178 | 179 | return topElem.get("balance").getAsLong(); 180 | } 181 | 182 | /** 183 | * Lists all active addresses in the wallet. 184 | * 185 | * @return A list of Address objects 186 | * @throws APIException If the server returns an error 187 | */ 188 | public List

listAddresses () throws APIException, IOException { 189 | Map params = buildBasicRequest(); 190 | 191 | String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/list", identifier), params); 192 | JsonObject topElem = parseResponse(response); 193 | 194 | List
addresses = new ArrayList
(); 195 | for (JsonElement jAddr : topElem.get("addresses").getAsJsonArray()) { 196 | JsonObject a = jAddr.getAsJsonObject(); 197 | Address address = new Address(a.get("balance").getAsLong(), a.get("address").getAsString(), a.has("label") && !a.get("label").isJsonNull() ? a.get("label").getAsString() : null, a.get("total_received").getAsLong()); 198 | 199 | addresses.add(address); 200 | } 201 | 202 | return addresses; 203 | } 204 | 205 | /** 206 | * Retrieves an address from the wallet. 207 | * 208 | * @param address Address in the wallet to look up 209 | * @return An instance of the Address class 210 | * @throws APIException If the server returns an error 211 | */ 212 | public Address getAddress (String address) throws APIException, IOException { 213 | Map params = buildBasicRequest(); 214 | params.put("address", address); 215 | 216 | String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/address_balance", identifier), params); 217 | JsonObject topElem = parseResponse(response); 218 | 219 | return new Address(topElem.get("balance").getAsLong(), topElem.get("address").getAsString(), topElem.has("label") && !topElem.get("label").isJsonNull() ? topElem.get("label").getAsString() : null, topElem.get("total_received").getAsLong()); 220 | } 221 | 222 | /** 223 | * Generates a new address and adds it to the wallet. 224 | * 225 | * @param label Label to attach to this address (optional, nullable) 226 | * @return An instance of the Address class 227 | * @throws APIException If the server returns an error 228 | */ 229 | public Address newAddress (String label) throws APIException, IOException { 230 | Map params = buildBasicRequest(); 231 | if (label != null) { 232 | params.put("label", label); 233 | } 234 | 235 | String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/new_address", identifier), params); 236 | JsonObject topElem = parseResponse(response); 237 | 238 | return new Address(0L, topElem.get("address").getAsString(), topElem.has("label") && !topElem.get("label").isJsonNull() ? topElem.get("label").getAsString() : null, 0L); 239 | } 240 | 241 | /** 242 | * Archives an address. 243 | * 244 | * @param address Address to archive 245 | * @return String representation of the archived address 246 | * @throws APIException If the server returns an error 247 | */ 248 | public String archiveAddress (String address) throws APIException, IOException { 249 | Map params = buildBasicRequest(); 250 | params.put("address", address); 251 | 252 | String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/archive_address", identifier), params); 253 | JsonObject topElem = parseResponse(response); 254 | 255 | return topElem.get("archived").getAsString(); 256 | } 257 | 258 | /** 259 | * Unarchives an address. 260 | * 261 | * @param address Address to unarchive 262 | * @return String representation of the unarchived address 263 | * @throws APIException If the server returns an error 264 | */ 265 | public String unarchiveAddress (String address) throws APIException, IOException { 266 | Map params = buildBasicRequest(); 267 | params.put("address", address); 268 | 269 | String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/unarchive_address", identifier), params); 270 | JsonObject topElem = parseResponse(response); 271 | 272 | return topElem.get("active").getAsString(); 273 | } 274 | 275 | private Map buildBasicRequest () { 276 | Map params = new HashMap(); 277 | 278 | params.put("password", password); 279 | if (apiCode != null) { 280 | params.put("api_code", apiCode); 281 | } 282 | 283 | return params; 284 | } 285 | 286 | private Map buildBasicRequestWithSecondPassword () { 287 | Map params = buildBasicRequest(); 288 | if (secondPassword != null) { 289 | params.put("second_password", secondPassword); 290 | } 291 | 292 | return params; 293 | } 294 | 295 | private JsonObject parseResponse (String response) throws APIException { 296 | JsonObject topElem = jsonParser.parse(response).getAsJsonObject(); 297 | if (topElem.has("error")) { 298 | throw new APIException(topElem.get("error").getAsString()); 299 | } 300 | 301 | return topElem; 302 | } 303 | 304 | @Override 305 | public boolean equals (Object o) { 306 | if (this == o) { 307 | return true; 308 | } 309 | if (o == null || getClass() != o.getClass()) { 310 | return false; 311 | } 312 | 313 | Wallet wallet = (Wallet) o; 314 | 315 | return identifier == null ? wallet.identifier == null : identifier.equals(wallet.identifier); 316 | } 317 | 318 | @Override 319 | public int hashCode () { 320 | return identifier != null ? identifier.hashCode() : 0; 321 | } 322 | 323 | } 324 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/wallet/entity/Address.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.wallet.entity; 2 | 3 | /** 4 | * Used in combination with the `Wallet` class 5 | */ 6 | public class Address { 7 | private long balance; 8 | private String address; 9 | private String label; 10 | private long totalReceived; 11 | 12 | public Address (long balance, String address, String label, long totalReceived) { 13 | this.balance = balance; 14 | this.address = address; 15 | this.label = label; 16 | this.totalReceived = totalReceived; 17 | } 18 | 19 | @Override 20 | public boolean equals (Object o) { 21 | if (this == o) { 22 | return true; 23 | } 24 | if (o == null || getClass() != o.getClass()) { 25 | return false; 26 | } 27 | 28 | Address address1 = (Address) o; 29 | 30 | if (balance != address1.balance) { 31 | return false; 32 | } 33 | if (totalReceived != address1.totalReceived) { 34 | return false; 35 | } 36 | if (address != null ? !address.equals(address1.address) : address1.address != null) { 37 | return false; 38 | } 39 | return !(label != null ? !label.equals(address1.label) : address1.label != null); 40 | 41 | } 42 | 43 | @Override 44 | public int hashCode () { 45 | int result = (int) (balance ^ (balance >>> 32)); 46 | result = 31 * result + (address != null ? address.hashCode() : 0); 47 | result = 31 * result + (label != null ? label.hashCode() : 0); 48 | result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32)); 49 | return result; 50 | } 51 | 52 | /** 53 | * @return Balance in satoshi 54 | */ 55 | public long getBalance () { 56 | return balance; 57 | } 58 | 59 | /** 60 | * @return String representation of the address 61 | */ 62 | public String getAddress () { 63 | return address; 64 | } 65 | 66 | /** 67 | * @return Label attached to the address 68 | */ 69 | public String getLabel () { 70 | return label; 71 | } 72 | 73 | /** 74 | * @return Total received amount in satoshi 75 | */ 76 | public long getTotalReceived () { 77 | return totalReceived; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/wallet/entity/CreateWalletResponse.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.wallet.entity; 2 | 3 | /** 4 | * Used in response to the `create` method in the `CreateWallet` class. 5 | */ 6 | public class CreateWalletResponse { 7 | private String identifier; 8 | private String address; 9 | private String label; 10 | 11 | public CreateWalletResponse (String identifier, String address, String label) { 12 | this.identifier = identifier; 13 | this.address = address; 14 | this.label = label; 15 | } 16 | 17 | /** 18 | * @return Wallet identifier (GUID) 19 | */ 20 | public String getIdentifier () { 21 | return identifier; 22 | } 23 | 24 | /** 25 | * @return First address in the wallet 26 | */ 27 | public String getAddress () { 28 | return address; 29 | } 30 | 31 | /** 32 | * @return Label of first address in the wallet 33 | */ 34 | public String getLable () { 35 | return label; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/info/blockchain/api/wallet/entity/PaymentResponse.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.wallet.entity; 2 | 3 | /** 4 | * Used in response to the `send` and `sendMany` methods in the `Wallet` class. 5 | */ 6 | public class PaymentResponse { 7 | private String message; 8 | private String txHash; 9 | private String notice; 10 | 11 | public PaymentResponse (String message, String txHash, String notice) { 12 | this.message = message; 13 | this.txHash = txHash; 14 | this.notice = notice; 15 | } 16 | 17 | @Override 18 | public boolean equals (Object o) { 19 | if (o == null) { 20 | return false; 21 | } 22 | if (o instanceof PaymentResponse) { 23 | PaymentResponse that = (PaymentResponse) o; 24 | return (this.getMessage().equals(that.getMessage()) && this.getTxHash().equals(that.getTxHash()) && this.getNotice().equals(that.getNotice())); 25 | } 26 | return false; 27 | } 28 | 29 | /** 30 | * @return Response message from the server 31 | */ 32 | public String getMessage () { 33 | return message; 34 | } 35 | 36 | /** 37 | * @return Transaction hash 38 | */ 39 | public String getTxHash () { 40 | return txHash; 41 | } 42 | 43 | /** 44 | * @return Additional response message from the server 45 | */ 46 | public String getNotice () { 47 | return notice; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/info/blockchain/api/AppTest.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest extends TestCase { 11 | /** 12 | * Create the test case 13 | * 14 | * @param testName name of the test case 15 | */ 16 | public AppTest (String testName) { 17 | super(testName); 18 | } 19 | 20 | /** 21 | * @return the suite of tests being tested 22 | */ 23 | public static Test suite () { 24 | return new TestSuite(AppTest.class); 25 | } 26 | 27 | /** 28 | * Rigourous Test :-) 29 | */ 30 | public void testApp () { 31 | assertTrue(true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/info/blockchain/api/blockexplorer/BlockExplorerTest.java: -------------------------------------------------------------------------------- 1 | package info.blockchain.api.blockexplorer; 2 | 3 | import info.blockchain.api.blockexplorer.entity.*; 4 | import org.junit.Assert; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * Created by ray on 10/05/2017. 16 | */ 17 | public class BlockExplorerTest { 18 | 19 | BlockExplorer client; 20 | 21 | @Before 22 | public void setUp () throws Exception { 23 | client = new BlockExplorer(); 24 | } 25 | 26 | @Test 27 | public void getAddress () throws Exception { 28 | Address address = client.getAddress("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", FilterType.All, 10, null); 29 | 30 | assertEquals("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", address.getAddress()); 31 | assertEquals("07feead7f9fb7d16a0251421ac9fa090169cc169", 32 | address.getHash160()); 33 | assertEquals(0, address.getFinalBalance()); 34 | assertEquals(16, address.getTxCount()); 35 | assertEquals(605204 , address.getTotalReceived()); 36 | assertEquals(605204 , address.getTotalSent()); 37 | assertEquals(10, address.getTransactions().size()); 38 | } 39 | 40 | @Test 41 | public void getUnspentOutputs () throws Exception { 42 | String address1 = "1FrWWFJ95Jq7EDgpkeBwVLAtoJMPwmYS7T"; 43 | String address2 = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn"; 44 | List unspentOutputs = client.getUnspentOutputs(Arrays.asList(address1, address2), 6, 10); 45 | 46 | assertTrue(unspentOutputs != null && unspentOutputs.size() != 0); 47 | assertEquals("2e7ab41818ee0ab987d393d4c8bf5e436b6e8c15ef3535a2b3eac581e33c7472", unspentOutputs.get(0).getTransactionHash()); 48 | assertEquals(20000, unspentOutputs.get(0).getValue()); 49 | } 50 | 51 | @Test 52 | public void getBalance () throws Exception { 53 | String address1 = "1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW"; 54 | String address2 = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn"; 55 | 56 | List list = Arrays.asList(address1, address2); 57 | 58 | Map balances = client.getBalance(list, FilterType.All); 59 | 60 | assertEquals(0, balances.get(address1).getFinalBalance()); 61 | assertEquals(16, balances.get(address1).getTxCount()); 62 | assertEquals(605204, balances.get(address1).getTotalReceived()); 63 | assertEquals(20000, balances.get(address2).getFinalBalance()); 64 | assertEquals(1, balances.get(address2).getTxCount()); 65 | assertEquals(20000, balances.get(address2).getTotalReceived()); 66 | } 67 | 68 | @Test 69 | public void getXpub () throws Exception { 70 | String address = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn"; 71 | XpubFull xpub = client.getXpub(address, null, null, null); 72 | 73 | assertEquals(xpub.getAddress(), 74 | "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn"); 75 | assertEquals(1, xpub.getTxCount()); 76 | assertEquals(20000, xpub.getTotalReceived()); 77 | assertEquals(0, xpub.getTotalSent()); 78 | assertEquals(20000, xpub.getFinalBalance()); 79 | assertEquals(0, xpub.getChangeIndex()); 80 | assertEquals(1, xpub.getAccountIndex()); 81 | assertEquals(0, xpub.getGapLimit()); 82 | } 83 | 84 | } --------------------------------------------------------------------------------