txs) {
this.txs = txs;
}
}
================================================
FILE: src/main/java/info/blockchain/api/etc/Base58.java
================================================
package info.blockchain.api.etc;
/**
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Arrays;
/**
* Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.
*
* Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.
*
* Satoshi explains: why base-58 instead of standard base-64 encoding?
*
* - Don't want 0OIl characters that look the same in some fonts and
* could be used to create visually identical looking account numbers.
* - A string with non-alphanumeric characters is not as easily accepted as an account number.
* - E-mail usually won't line-break if there's no punctuation to break at.
* - Doubleclicking selects the whole number as one word if it's all alphanumeric.
*
*
* However, note that the encoding/decoding runs in O(n²) time, so it is not useful for large data.
*
* The basic idea of the encoding is to treat the data bytes as a large number represented using
* base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
* number of leading zeros (which are otherwise lost during the mathematical operations on the
* numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
*/
public class Base58 {
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char ENCODED_ZERO = ALPHABET[0];
private static final int[] INDEXES = new int[128];
static {
Arrays.fill(INDEXES, -1);
for (int i = 0; i < ALPHABET.length; i++) {
INDEXES[ALPHABET[i]] = i;
}
}
/**
* Encodes the given bytes as a base58 string (no checksum is appended).
*
* @param input the bytes to encode
* @return the base58-encoded string
*/
public static String encode (byte[] input) {
if (input.length == 0) {
return "";
}
// Count leading zeros.
int zeros = 0;
while (zeros < input.length && input[zeros] == 0) {
++zeros;
}
// Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
input = Arrays.copyOf(input, input.length); // since we modify it in-place
char[] encoded = new char[input.length * 2]; // upper bound
int outputStart = encoded.length;
for (int inputStart = zeros; inputStart < input.length; ) {
encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
if (input[inputStart] == 0) {
++inputStart; // optimization - skip leading zeros
}
}
// Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
++outputStart;
}
while (--zeros >= 0) {
encoded[--outputStart] = ENCODED_ZERO;
}
// Return encoded string (including encoded leading zeros).
return new String(encoded, outputStart, encoded.length - outputStart);
}
/**
* Decodes the given base58 string into the original data bytes.
*
* @param input the base58-encoded string to decode
* @return the decoded data bytes
* @throws ParseException if the given string is not a valid base58 string
*/
public static byte[] decode (String input) throws ParseException {
if (input.length() == 0) {
return new byte[0];
}
// Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
byte[] input58 = new byte[input.length()];
for (int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);
int digit = c < 128 ? INDEXES[c] : -1;
if (digit < 0) {
throw new ParseException("Illegal character " + c + " at position " + i, i);
}
input58[i] = (byte) digit;
}
// Count leading zeros.
int zeros = 0;
while (zeros < input58.length && input58[zeros] == 0) {
++zeros;
}
// Convert base-58 digits to base-256 digits.
byte[] decoded = new byte[input.length()];
int outputStart = decoded.length;
for (int inputStart = zeros; inputStart < input58.length; ) {
decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
if (input58[inputStart] == 0) {
++inputStart; // optimization - skip leading zeros
}
}
// Ignore extra leading zeroes that were added during the calculation.
while (outputStart < decoded.length && decoded[outputStart] == 0) {
++outputStart;
}
// Return decoded data (including original number of leading zeros).
return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
}
public static BigInteger decodeToBigInteger (String input) throws ParseException {
return new BigInteger(1, decode(input));
}
/**
* Decodes the given base58 string into the original data bytes, using the checksum in the
* last 4 bytes of the decoded data to verify that the rest are correct. The checksum is
* removed from the returned data.
*
* @param input the base58-encoded string to decode (which should include the checksum)
* @throws ParseException if the input is not base 58 or the checksum does not validate.
*/
public static byte[] decodeChecked (String input) throws ParseException {
byte[] decoded = decode(input);
if (decoded.length < 4) {
throw new ParseException("Input too short", 0);
}
byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
byte[] actualChecksum = Arrays.copyOfRange(hashTwice(data), 0, 4);
if (!Arrays.equals(checksum, actualChecksum)) {
throw new ParseException("Checksum does not validate", 0);
}
return data;
}
/**
* Divides a number, represented as an array of bytes each containing a single digit
* in the specified base, by the given divisor. The given number is modified in-place
* to contain the quotient, and the return value is the remainder.
*
* @param number the number to divide
* @param firstDigit the index within the array of the first non-zero digit
* (this is used for optimization by skipping the leading zeros)
* @param base the base in which the number's digits are represented (up to 256)
* @param divisor the number to divide by (up to 256)
* @return the remainder of the division operation
*/
private static byte divmod (byte[] number, int firstDigit, int base, int divisor) {
// this is just long division which accounts for the base of the input digits
int remainder = 0;
for (int i = firstDigit; i < number.length; i++) {
int digit = (int) number[i] & 0xFF;
int temp = remainder * base + digit;
number[i] = (byte) (temp / divisor);
remainder = temp % divisor;
}
return (byte) remainder;
}
public static byte[] hashTwice (byte[] input) {
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
digest.update(input, 0, input.length);
return digest.digest(digest.digest());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
================================================
FILE: src/main/java/info/blockchain/api/exchangerates/Currency.java
================================================
package info.blockchain.api.exchangerates;
import java.math.BigDecimal;
/**
* Used in response to the `getTicker` method in the `ExchangeRates` class.
*/
public class Currency {
private BigDecimal buy;
private BigDecimal sell;
private BigDecimal last;
private BigDecimal price15m;
private String symbol;
public Currency (double buy, double sell, double last, double price15m, String symbol) {
this.buy = BigDecimal.valueOf(buy);
this.sell = BigDecimal.valueOf(sell);
this.last = BigDecimal.valueOf(last);
this.price15m = BigDecimal.valueOf(price15m);
this.symbol = symbol;
}
/**
* @return Current buy price
*/
public BigDecimal getBuy () {
return buy;
}
/**
* @return Current sell price
*/
public BigDecimal getSell () {
return sell;
}
/**
* @return Most recent market price
*/
public BigDecimal getLast () {
return last;
}
/**
* @return 15 minutes delayed market price
*/
public BigDecimal getPrice15m () {
return price15m;
}
/**
* @return Currency symbol
*/
public String getSymbol () {
return symbol;
}
}
================================================
FILE: src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java
================================================
package info.blockchain.api.exchangerates;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* This class reflects the functionality documented
* at https://blockchain.info/api/exchange_rates_api. It allows users to fetch the latest
* ticker data and convert amounts between BTC and fiat currencies.
*/
public class ExchangeRates {
private final String apiCode;
public ExchangeRates () {
this(null);
}
public ExchangeRates (String apiCode) {
this.apiCode = apiCode;
}
/**
* Gets the price ticker from https://blockchain.info/ticker
*
* @return A map of currencies where the key is a 3-letter currency symbol and the
* value is the `Currency` class
* @throws APIException If the server returns an error
*/
public Map getTicker () throws APIException, IOException {
Map params = new HashMap();
if (apiCode != null) {
params.put("api_code", apiCode);
}
String response = HttpClient.getInstance().get("ticker", params);
JsonObject ticker = new JsonParser().parse(response).getAsJsonObject();
Map resultMap = new HashMap();
for (Entry ccyKVP : ticker.entrySet()) {
JsonObject ccy = ccyKVP.getValue().getAsJsonObject();
Currency currency = new Currency(ccy.get("buy").getAsDouble(), ccy.get("sell").getAsDouble(), ccy.get("last").getAsDouble(), ccy.get("15m").getAsDouble(), ccy.get("symbol").getAsString());
resultMap.put(ccyKVP.getKey(), currency);
}
return resultMap;
}
/**
* Converts x value in the provided currency to BTC.
*
* @param currency Currency code
* @param value Value to convert
* @return Converted value in BTC
* @throws APIException If the server returns an error
*/
public BigDecimal toBTC (String currency, BigDecimal value) throws APIException, IOException {
Map params = new HashMap();
params.put("currency", currency);
params.put("value", String.valueOf(value));
if (apiCode != null) {
params.put("api_code", apiCode);
}
String response = HttpClient.getInstance().get("tobtc", params);
return new BigDecimal(response);
}
/**
* Converts x value in BTC to the provided currency.
*
* @param currency Currency code
* @param value Value to convert
* @return Converted value in the provided currency
* @throws APIException If the server returns an error
*/
public BigDecimal toFiat (String currency, BigDecimal value) throws APIException, IOException {
Map params = new HashMap();
params.put("currency", currency);
params.put("value", String.valueOf(value.multiply(BigDecimal.valueOf(100000000L)))); // The endpoint is expecting satoshi
if (apiCode != null) {
params.put("api_code", apiCode);
}
String response = HttpClient.getInstance().get("frombtc", params);
return new BigDecimal(response);
}
}
================================================
FILE: src/main/java/info/blockchain/api/pushtx/PushTx.java
================================================
package info.blockchain.api.pushtx;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* This class reflects the functionality provided at
* https://blockchain.info/pushtx. It allows users to broadcast hex encoded
* transactions to the bitcoin network.
*/
public class PushTx {
/**
* Pushes a hex encoded transaction to the network.
*
* @param tx Hex encoded transaction
* @throws APIException If the server returns an error (malformed tx etc.)
*/
public static void pushTx (String tx) throws APIException, IOException {
pushTx(tx, null);
}
/**
* Pushes a hex encoded transaction to the network.
*
* @param tx Hex encoded transaction
* @param apiCode Blockchain.info API code (optional, nullable)
* @throws APIException If the server returns an error (malformed tx etc.)
*/
public static void pushTx (String tx, String apiCode) throws APIException, IOException {
Map params = new HashMap();
params.put("tx", tx);
if (apiCode != null) {
params.put("api_code", apiCode);
}
HttpClient.getInstance().post("pushtx", params);
}
}
================================================
FILE: src/main/java/info/blockchain/api/receive/CallbackLog.java
================================================
package info.blockchain.api.receive;
import com.google.gson.JsonObject;
public class CallbackLog {
private String callback;
private String callbackTime;
private String response;
private int responseCode;
public CallbackLog (JsonObject callbackJson) {
this(
callbackJson.has("callback") ? callbackJson.get("callback").getAsString() : "",
callbackJson.has("called_at") ? callbackJson.get("called_at").getAsString() : "",
callbackJson.has("raw_response") ? callbackJson.get("raw_response").getAsString() : "",
callbackJson.has("response_code") ? callbackJson.get("response_code").getAsInt() : 0
);
}
public CallbackLog (String callback, String callbackTime, String response, int responseCode) {
this.callback = callback;
this.callbackTime = callbackTime;
this.response = response;
this.responseCode = responseCode;
}
public String getCallback () {
return callback;
}
public void setCallback (String callback) {
this.callback = callback;
}
public String getCallbackTime () {
return callbackTime;
}
public void setCallbackTime (String callbackTime) {
this.callbackTime = callbackTime;
}
public String getResponse () {
return response;
}
public void setResponse (String response) {
this.response = response;
}
public int getResponseCode () {
return responseCode;
}
public void setResponseCode (int responseCode) {
this.responseCode = responseCode;
}
}
================================================
FILE: src/main/java/info/blockchain/api/receive/Receive.java
================================================
package info.blockchain.api.receive;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* This class reflects the functionality necessary for using the receive-payments-api v2.
* Passing on a xPUB, callbackUrl and the apiCode will return an address for receiving a payment.
*
* Upon receiving a payment on this address, the merchant will be notified using the callback URL.
*/
public class Receive {
private final String apiCode;
/**
* @param apiCode Blockchain.info API code for the receive-payments v2 API (different from normal API key)
*/
public Receive (String apiCode) {
this.apiCode = apiCode;
}
/**
* Calls the receive-payments-api v2 and returns an address for the payment.
*
* @param xPUB Destination address where the payment should be sent
* @param callbackUrl Callback URI that will be called upon payment
* @return An instance of the ReceiveV2Response class
* @throws APIException If the server returns an error
*/
public ReceiveResponse receive (String xPUB, String callbackUrl) throws APIException, IOException {
Map params = new HashMap();
params.put("xpub", xPUB);
params.put("callback", callbackUrl);
params.put("key", apiCode);
String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive", params);
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(response).getAsJsonObject();
return new ReceiveResponse(obj.get("index").getAsInt(), obj.get("address").getAsString(), obj.get("callback").getAsString());
}
/**
* Calls the receive-payments-api v2 and returns the xpub gap of an xpub.
*
* @param xPUB Destination address where the payment should be sent
* @return An instance of the ReceiveV2Response class
* @throws APIException If the server returns an error
*/
public int checkGap (String xPUB) throws APIException, IOException {
Map params = new HashMap();
params.put("xpub", xPUB);
params.put("key", apiCode);
String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/checkgap", params);
JsonObject obj = new JsonParser().parse(response).getAsJsonObject();
return obj.get("gap").getAsInt();
}
/**
* Calls the receive-payments-api v2 and returns the callback log based on url.
*
* @param callbackUrl Callback URI that will be called upon payment
* @return An instance of the ReceiveV2Response class
* @throws APIException If the server returns an error
*/
public CallbackLog getCallbackLog (String callbackUrl) throws APIException, IOException {
Map params = new HashMap();
params.put("callback", callbackUrl);
params.put("key", apiCode);
String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/callback", params);
JsonObject obj = new JsonParser().parse(response).getAsJsonObject();
return new CallbackLog(obj);
}
}
================================================
FILE: src/main/java/info/blockchain/api/receive/ReceiveResponse.java
================================================
package info.blockchain.api.receive;
/**
* This class is used as a response object to the `ReceiveV2.receive` method.
*/
public class ReceiveResponse {
private int index;
private String receivingAddress;
private String callbackUrl;
public ReceiveResponse (int index, String receivingAddress, String callbackUrl) {
this.index = index;
this.receivingAddress = receivingAddress;
this.callbackUrl = callbackUrl;
}
/**
* @return Index of the address in the account
*/
public int getIndex () {
return index;
}
/**
* @return Address to be displayed for the customer at checkout.
*/
public String getReceivingAddress () {
return receivingAddress;
}
/**
* @return Callback URI that will be called upon payment
*/
public String getCallbackUrl () {
return callbackUrl;
}
}
================================================
FILE: src/main/java/info/blockchain/api/statistics/Chart.java
================================================
package info.blockchain.api.statistics;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.List;
public class Chart {
private String status;
private String name;
private String unit;
private String period;
private String description;
private List values;
public Chart (JsonObject chartJson) {
this(
chartJson.has("status") ? chartJson.get("status").getAsString() : "",
chartJson.has("name") ? chartJson.get("name").getAsString() : "",
chartJson.has("unit") ? chartJson.get("unit").getAsString() : "",
chartJson.has("period") ? chartJson.get("period").getAsString() : "",
chartJson.has("description") ? chartJson.get("description").getAsString() : "",
null
);
values = getPoints(chartJson);
}
public Chart (String status, String name, String unit, String period, String description, List values) {
this.status = status;
this.name = name;
this.unit = unit;
this.period = period;
this.description = description;
this.values = values;
}
private List getPoints(JsonObject chartJson) {
List points = new ArrayList();
for (JsonElement pointElement : chartJson.getAsJsonArray()) {
JsonObject pointJson = pointElement.getAsJsonObject();
points.add(new Point(pointJson));
}
return points;
}
public String getStatus() {
return status;
}
public String getName() {
return name;
}
public String getUnit() {
return unit;
}
public String getPeriod() {
return period;
}
public String getDescription() {
return description;
}
public List getValues() {
return values;
}
}
================================================
FILE: src/main/java/info/blockchain/api/statistics/Point.java
================================================
package info.blockchain.api.statistics;
import com.google.gson.JsonObject;
public class Point {
private float x;
private float y;
public Point (JsonObject pointJson) {
this(
pointJson.has("x") ? pointJson.get("x").getAsFloat() : 0.0F,
pointJson.has("y") ? pointJson.get("y").getAsFloat() : 0.0F
);
}
public Point (float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
================================================
FILE: src/main/java/info/blockchain/api/statistics/Statistics.java
================================================
package info.blockchain.api.statistics;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
/**
* This class reflects the functionality documented
* at https://blockchain.info/api/charts_api
*/
public class Statistics {
private final String apiCode;
public Statistics () {
this(null);
}
public Statistics (String apiCode) {
this.apiCode = apiCode;
}
/**
* Gets the network statistics.
*
* @return An instance of the StatisticsResponse class
* @throws APIException If the server returns an error
*/
public StatisticsResponse getStats () throws APIException, IOException {
Map params = new HashMap();
params.put("format", "json");
if (apiCode != null) {
params.put("api_code", apiCode);
}
String response = HttpClient.getInstance().get("stats", params);
return new StatisticsResponse(response);
}
/**
* This method can be used to get and manipulate data behind all Blockchain.info's charts.
*
* @param type of chart (Example: transactions-per-second, total-bitcoins)
* @param timeSpan (Example: 5weeks)
* @param rollingAverage (Example: 8hours)
* @return {@code Chart} represents the series of data of the chart
* @throws APIException If the server returns an error
*/
public Chart getChart(String type, String timeSpan, String rollingAverage) throws APIException, IOException {
Map params = new HashMap();
params.put("format", "json");
if (apiCode != null) {
params.put("api_code", apiCode);
}
if (timeSpan != null) {
params.put("timespan", timeSpan);
}
if (rollingAverage != null) {
params.put("rollingAverage", rollingAverage);
}
String response = HttpClient.getInstance().get("charts/" + type, params);
JsonObject chartJson = new JsonParser().parse(response).getAsJsonObject();
return new Chart(chartJson);
}
/**
* This method can be used to get the data behind Blockchain.info's pools information.
*
* @param timeSpan (Example: 5weeks)
* @return a map of pool name and the number of blocks it mined
* @throws APIException If the server returns an error
*/
public Map getPools(String timeSpan) throws APIException, IOException {
Map params = new HashMap();
params.put("format", "json");
if (apiCode != null) {
params.put("api_code", apiCode);
}
if (timeSpan != null) {
params.put("timespan", timeSpan);
}
String response = HttpClient.getInstance().get("pools", params);
Type type = new TypeToken