package network.thunder.core.communication.objects.messages.impl.blockchainlistener.bciapi; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import network.thunder.core.etc.Constants; import network.thunder.core.etc.Tools; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * The BlockExplorer class 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. */ public class BlockExplorer { private String apiCode; public BlockExplorer () { this(null); } /** * @param apiCode Blockchain.info API code (optional, nullable) */ public BlockExplorer (String apiCode) { this.apiCode = apiCode; } /** * Gets a single transaction based on a transaction index. * * @param txIndex Transaction index * @return An instance of the {@link Transaction} class * @throws APIException If the server returns an error */ public Transaction getTransaction (long txIndex) throws APIException, IOException { return getTransaction(String.valueOf(txIndex)); } /** * Gets a single transaction based on a transaction hash. * * @param txHash Transaction hash * @return An instance of the {@link Transaction} class * @throws APIException If the server returns an error */ public Transaction getTransaction (String txHash) throws APIException, IOException { String response = HttpClient.getInstance().get("rawtx/" + txHash + "?api_code=" + apiCode, null); JsonObject txJson = new JsonParser().parse(response).getAsJsonObject(); return new Transaction(txJson); } public org.bitcoinj.core.Transaction getBitcoinJTransaction (String txHash) throws APIException, IOException { String response = HttpClient.getInstance().get("rawtx/" + txHash + "?api_code=" + apiCode + "&format=hex", null); byte[] raw = Tools.hexStringToByteArray(response); return new org.bitcoinj.core.Transaction(Constants.getNetwork(), raw); } /** * Gets a single block based on a block index. * * @param blockIndex Block index * @return An instance of the {@link Block} class * @throws APIException If the server returns an error */ public Block getBlock (long blockIndex) throws APIException, IOException { return getBlock(String.valueOf(blockIndex)); } /** * Gets a single block based on a block hash. * * @param blockHash Block hash * @return An instance of the {@link Block} class * @throws APIException If the server returns an error */ public Block getBlock (String blockHash) throws APIException, IOException { String response = HttpClient.getInstance().get("rawblock/" + blockHash + "?api_code=" + apiCode, null); JsonObject blockJson = new JsonParser().parse(response).getAsJsonObject(); return new Block(blockJson); } /** * Gets data for a single address. * * @param address Base58check or hash160 address string * @return An instance of the {@link Address} class * @throws APIException If the server returns an error */ public Address getAddress (String address) throws APIException, IOException { String response = HttpClient.getInstance().get("rawaddr/" + address + "?api_code=" + apiCode, null); JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject(); return new Address(addrJson); } /** * Gets a list of blocks at the specified height. Normally, only one block will be * returned, but in case of a chain fork, multiple blocks may be present. * * @param height Block height * @return A list of blocks at the specified height * @throws APIException If the server returns an error */ public List<Block> getBlocksAtHeight (long height) throws APIException, IOException { List<Block> blocks = new ArrayList<Block>(); Map<String, String> params = new HashMap<String, String>(); params.put("format", "json"); if (apiCode != null) { params.put("api_code", apiCode); } String response = HttpClient.getInstance().get("block-height/" + height, params); JsonObject blocksJson = new JsonParser().parse(response).getAsJsonObject(); for (JsonElement blockElem : blocksJson.get("blocks").getAsJsonArray()) { blocks.add(new Block(blockElem.getAsJsonObject())); } return blocks; } /** * Gets unspent outputs for a single address. * * @param address Base58check or hash160 address string * @return A list of unspent outputs for the specified address * @throws APIException If the server returns an error */ public List<UnspentOutput> getUnspentOutputs (String address) throws APIException, IOException { List<UnspentOutput> outputs = new ArrayList<UnspentOutput>(); Map<String, String> params = new HashMap<String, String>(); params.put("active", address); if (apiCode != null) { params.put("api_code", apiCode); } String response = null; try { response = HttpClient.getInstance().get("unspent", params); } catch (APIException e) { // the API isn't supposed to return an error code here. No free outputs is // a legitimate situation. We are circumventing that by returning an empty list if (e.getMessage().equals("No free outputs to spend")) { return outputs; } else { throw e; } } JsonObject outsJson = new JsonParser().parse(response).getAsJsonObject(); for (JsonElement outElem : outsJson.get("unspent_outputs").getAsJsonArray()) { outputs.add(new UnspentOutput(outElem.getAsJsonObject())); } return outputs; } /** * Gets the latest block on the main chain (simplified representation). * * @return An instance of the {@link LatestBlock} class * @throws APIException If the server returns an error */ public LatestBlock getLatestBlock () throws APIException, IOException { String response = HttpClient.getInstance().get("latestblock?api_code=" + apiCode, null); JsonObject blockObj = new JsonParser().parse(response).getAsJsonObject(); return new LatestBlock(blockObj); } /** * Gets a list of currently unconfirmed transactions. * * @return A list of unconfirmed {@link Transaction} objects * @throws APIException If the server returns an error */ public List<Transaction> getUnconfirmedTransactions () throws APIException, IOException { List<Transaction> transactions = new ArrayList<Transaction>(); Map<String, String> params = new HashMap<String, String>(); params.put("format", "json"); if (apiCode != null) { params.put("api_code", apiCode); } String response = HttpClient.getInstance().get("unconfirmed-transactions", params); JsonObject txList = new JsonParser().parse(response).getAsJsonObject(); for (JsonElement txElem : txList.get("txs").getAsJsonArray()) { JsonObject txObj = txElem.getAsJsonObject(); transactions.add(new Transaction(txObj, -1, txObj.get("double_spend").getAsBoolean())); } return transactions; } /** * Gets a list of blocks mined today by all pools since 00:00 UTC. * * @return A list of {@link SimpleBlock} objects * @throws APIException APIException If the server returns an error */ public List<SimpleBlock> getBlocks () throws APIException, IOException { return getBlocks(null); } /** * Gets a list of blocks mined on a specific day. * * @param timestamp Unix timestamp (without milliseconds) that falls between * 00:00 UTC and 23:59 UTC of the desired day. * @return A list of {@link SimpleBlock} objects */ public List<SimpleBlock> getBlocks (long timestamp) throws APIException, IOException { return getBlocks(String.valueOf(timestamp * 1000)); } /** * Gets a list of recent blocks by a specific mining pool. * * @param poolName Name of the mining pool * @return A list of {@link SimpleBlock} objects * @throws APIException If the server returns an error */ public List<SimpleBlock> getBlocks (String poolName) throws APIException, IOException { List<SimpleBlock> blocks = new ArrayList<SimpleBlock>(); poolName = poolName == null ? "" : poolName; Map<String, String> params = new HashMap<String, String>(); params.put("format", "json"); if (apiCode != null) { params.put("api_code", apiCode); } String response = HttpClient.getInstance().get("blocks/" + poolName, params); JsonObject blockList = new JsonParser().parse(response).getAsJsonObject(); for (JsonElement blockElem : blockList.get("blocks").getAsJsonArray()) { blocks.add(new SimpleBlock(blockElem.getAsJsonObject())); } return blocks; } /** * Gets inventory data for an object. * * @param hash Object hash * @return An instance of the {@link InventoryData} class * @throws APIException If the server returns an error */ public InventoryData getInventoryData (String hash) throws APIException, IOException { Map<String, String> params = new HashMap<String, String>(); params.put("format", "json"); if (apiCode != null) { params.put("api_code", apiCode); } String response = HttpClient.getInstance().get("inv/" + hash, params); JsonObject invObj = new JsonParser().parse(response).getAsJsonObject(); return new InventoryData(invObj); } }