dk.clanie.bitcoin.client.BitcoindClientImpl.java Source code

Java tutorial

Introduction

Here is the source code for dk.clanie.bitcoin.client.BitcoindClientImpl.java

Source

/*
 * Copyright (C) 2013, Claus Nielsen, cn@cn-consult.dk
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package dk.clanie.bitcoin.client;

import static dk.clanie.collections.CollectionFactory.newArrayList;
import static dk.clanie.collections.CollectionFactory.newHashMap;
import static dk.clanie.util.Util.firstNotNull;
import static java.lang.Boolean.FALSE;
import static java.util.Collections.EMPTY_LIST;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import dk.clanie.bitcoin.AddressAndAmount;
import dk.clanie.bitcoin.SignatureHashAlgorithm;
import dk.clanie.bitcoin.TransactionOutputRef;
import dk.clanie.bitcoin.client.request.AddNodeAction;
import dk.clanie.bitcoin.client.request.BitcoindJsonRpcRequest;
import dk.clanie.bitcoin.client.request.TemplateRequest;
import dk.clanie.bitcoin.client.response.BigDecimalResponse;
import dk.clanie.bitcoin.client.response.BooleanResponse;
import dk.clanie.bitcoin.client.response.CreateMultiSigResponse;
import dk.clanie.bitcoin.client.response.DecodeRawTransactionResponse;
import dk.clanie.bitcoin.client.response.GetAddedNodeInfoResponse;
import dk.clanie.bitcoin.client.response.GetBlockResponse;
import dk.clanie.bitcoin.client.response.GetBlockTemplateResponse;
import dk.clanie.bitcoin.client.response.GetInfoResponse;
import dk.clanie.bitcoin.client.response.GetMiningInfoResponse;
import dk.clanie.bitcoin.client.response.GetPeerInfoResponse;
import dk.clanie.bitcoin.client.response.GetRawTransactionResponse;
import dk.clanie.bitcoin.client.response.GetTransactionResponse;
import dk.clanie.bitcoin.client.response.GetTxOutResponse;
import dk.clanie.bitcoin.client.response.GetTxOutSetInfoResponse;
import dk.clanie.bitcoin.client.response.GetWorkResponse;
import dk.clanie.bitcoin.client.response.IntegerResponse;
import dk.clanie.bitcoin.client.response.ListAccountsResponse;
import dk.clanie.bitcoin.client.response.ListAddressGroupingsResponse;
import dk.clanie.bitcoin.client.response.ListLockUnspentResponse;
import dk.clanie.bitcoin.client.response.ListReceivedByAccountResponse;
import dk.clanie.bitcoin.client.response.ListReceivedByAddressResponse;
import dk.clanie.bitcoin.client.response.ListSinceBlockResponse;
import dk.clanie.bitcoin.client.response.ListTransactionsResponse;
import dk.clanie.bitcoin.client.response.ListUnspentResponse;
import dk.clanie.bitcoin.client.response.LongResponse;
import dk.clanie.bitcoin.client.response.SignRawTransactionResponse;
import dk.clanie.bitcoin.client.response.StringArrayResponse;
import dk.clanie.bitcoin.client.response.StringResponse;
import dk.clanie.bitcoin.client.response.ValidateAddressResponse;
import dk.clanie.bitcoin.client.response.VoidResponse;

/**
 * Implements bitcoind client providing java style functions for calling bitcoind rest-rpc methods.
 * 
 * @author Claus Nielsen
 */
@Service
public class BitcoindClientImpl implements BitcoindClient {

    // [Configuration]
    private String url;

    // [Collaborators]
    @Autowired
    private RestTemplate restTemplate;

    /**
     * Default constructor.
     */
    public BitcoindClientImpl() {
    }

    /**
     * Sets url for calling bitcoind.
     * 
     * @param url
     */
    @Override
    @Required
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * Add a nrequired-to-sign multisignature address to the wallet.
     * <p>
     * Each key is a bitcoin address or hex-encoded public key. If <code>account</code>
     * is specified, the new address is assigned to the given account.
     * 
     * @param nrequired - number of signatures required.
     * @param keys - keys which may sign. Each is a bitcoin address or a hex-encoded public key.
     * @param account optional. If given the new address is assigned to this account.
     * @return {@link StringResponse}.
     */
    @Override
    public StringResponse addMultiSigAddress(int nrequired, List<String> keys, String account) {
        List<Object> params = newArrayList();
        params.add(nrequired);
        params.add(keys);
        if (account != null)
            params.add(account);
        return jsonRpc("addmultisigaddress", params, StringResponse.class);
    }

    /**
     * Attempts add or remove <node> from the addnode list or try a connection
     * to &lt;node&gt; once.
     * 
     * @param node
     *            - host name or IP addres
     * @param action
     *            - what to do
     * @return {@link VoidResponse}
     * 
     * @see #getAddedNodeInfo(Boolean, String)
     * 
     * @since bitcoind 0.8
     */
    @Override
    public VoidResponse addNode(String node, AddNodeAction action) {
        List<Object> params = newArrayList();
        params.add(node);
        params.add(action.toString());
        return jsonRpc("addnode", params, VoidResponse.class);
    }

    /**
     * Safely copies wallet.dat to destination.
     * <p>
     * Destination can be a directory or a path with filename.
     * 
     * @param destination - directory or filename.
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse backupWallet(String destination) {
        List<Object> params = newArrayList();
        params.add(destination);
        return jsonRpc("backupwallet", params, VoidResponse.class);

    }

    /**
     * Creates a multi-signature address.
     * <p>
     * This is just like "addmultisigaddress" but instead of adding the multisig
     * address/redeemScript to the wallet, returns them in a object.
     * 
     * @param nRequired
     *            - number of signatures required.
     * @param keys
     *            -
     * @return {@link CreateMultiSigResponse}
     */
    @Override
    public CreateMultiSigResponse createMultiSig(Integer nRequired, String[] keys) {
        List<Object> params = newArrayList();
        params.add(nRequired);
        if (keys != null)
            params.add(keys);
        return jsonRpc("createmultisig", params, CreateMultiSigResponse.class);
    }

    /**
     * Creates a raw transaction for spending given inputs.
     * 
     * Create a transaction spending given {@link TransactionOutputRef}, for
     * sending to given address(es).<br>
     * Note that the transaction's inputs are not signed, and it is not stored
     * in the wallet or transmitted to the network.<br>
     * 
     * @param txOutputs
     *            - transaction outputs to spend
     * @param addressAndAmount
     *            - recipient and amount
     * @return {@link StringResponse} containing hex-encoded raw
     *         transaction.
     */
    @Override
    public StringResponse createRawTransaction(List<TransactionOutputRef> txOutputs,
            AddressAndAmount... addressAndAmount) {
        Map<String, BigDecimal> recipients = newHashMap();
        for (AddressAndAmount aaa : addressAndAmount) {
            String address = aaa.getAddress();
            BigDecimal amount = aaa.getAmount();
            if (recipients.containsKey(address)) {
                amount = recipients.get(address).add(amount);
            }
            recipients.put(address, amount);
        }
        List<Object> params = newArrayList();
        params.add(txOutputs);
        params.add(recipients);
        return jsonRpc("createrawtransaction", params, StringResponse.class);
    }

    /**
     * Produces a human-readable JSON object for a raw transaction
     * 
     * @param rawTransaction
     * @return {@link DecodeRawTransactionResponse}
     */
    @Override
    public DecodeRawTransactionResponse decodeRawTransaction(String rawTransaction) {
        List<Object> params = newArrayList();
        params.add(rawTransaction);
        return jsonRpc("decoderawtransaction", params, DecodeRawTransactionResponse.class);
    }

    /**
     * Reveals the private key corresponding to the given bitcoin address.
     * 
     * Requires unlocked wallet.
     * 
     * @param bitcoinAddress
     * @return {@link StringResponse}
     */
    @Override
    public StringResponse dumpPrivateKey(String bitcoinAddress) {
        List<String> params = newArrayList();
        params.add(bitcoinAddress);
        return jsonRpc("dumpprivkey", params, StringResponse.class);
    }

    /**
     * Encrypts the wallet with the given pass phrase.
     * 
     * @param passPhrase
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse encryptWallet(String passPhrase) {
        List<String> params = newArrayList();
        params.add(passPhrase);
        return jsonRpc("encryptwallet", params, VoidResponse.class);
    }

    /**
     * Returns the account associated with the given address.
     * 
     * @param bitcoinAddress
     * @return {@link StringResponse}
     */
    @Override
    public StringResponse getAccount(String bitcoinAddress) {
        List<String> params = newArrayList();
        params.add(bitcoinAddress);
        return jsonRpc("getaccount", params, StringResponse.class);
    }

    /**
     * Gets the current bitcoin address for receiving payments to the given account.
     * 
     * @param account
     * @return {@link StringResponse}
     */
    @Override
    public StringResponse getAccountAddress(String account) {
        List<String> params = newArrayList();
        params.add(account);
        return jsonRpc("getaccountaddress", params, StringResponse.class);
    }

    /**
     * Returns information about the given added node, or all added nodes (note
     * that onetry addnodes are not listed here).
     * 
     * @param dns
     *            - If dns is false, only a list of added nodes will be
     *            provided, otherwise connected information will also be
     *            available.
     * @param node
     *            - optional (may be null).
     * @return {@link GetAddedNodeInfoResponse}
     * 
     * @since bitcoind 0.8
     */
    @Override
    public GetAddedNodeInfoResponse getAddedNodeInfo(Boolean dns, String node) {
        // TODO When calling with dns=false an object is returned; when calling with dns=true an array is returned.
        // TODO Currently only the array case (dns=true) is handled - see  https://github.com/bitcoin/bitcoin/issues/2467
        // TODO If bitcoind isn't changed (bug 2467) implement special serialization of the response in _GetAddedNodeInfoResponse_dnsArgFalse.json
        List<Object> params = newArrayList();
        params.add(dns);
        if (node != null)
            params.add(node);
        return jsonRpc("getaddednodeinfo", params, GetAddedNodeInfoResponse.class);
    }

    /**
     * Returns the list of addresses for the given account.
     * 
     * @param account
     * @return {@link StringArrayResponse} with bitcoin addresses.
     */
    @Override
    public StringArrayResponse getAddressesByAccount(String account) {
        List<Object> params = newArrayList();
        params.add(account);
        return jsonRpc("getaddressesbyaccount", params, StringArrayResponse.class);
    }

    /**
     * Gets the balance of the given account or the server's total balance.
     * 
     * @param account
     *            - optional (may be null). If specified, returns the balance in
     *            the account. If not, returns the server's total available
     *            balance.
     * @param minConf
     *            - optional (may be null). Minim number of confirmations.
     * @return {@link BigDecimalResponse}
     */
    @Override
    public BigDecimalResponse getBalance(String account, Integer minConf) {
        List<Object> params = newArrayList();
        if (account != null || minConf != null)
            params.add(account);
        if (minConf != null)
            params.add(minConf);
        return jsonRpc("getbalance", params, BigDecimalResponse.class);
    }

    /**
     * Returns information about the given block hash.
     * 
     * @param hash - block hash
     * @return {@link GetBlockResponse}
     */
    @Override
    public GetBlockResponse getBlock(String hash) {
        List<Object> params = newArrayList();
        params.add(hash);
        return jsonRpc("getblock", params, GetBlockResponse.class);
    }

    /**
     * Returns the number of blocks in the longest block chain.
     * 
     * @return {@link LongResponse} with number of blocks in the longest block chain.
     */
    @Override
    public LongResponse getBlockCount() {
        return jsonRpc("getblockcount", EMPTY_LIST, LongResponse.class);
    }

    /**
     * Returns hash of block in best-block-chain at given index.
     * 
     * @param index
     * @return {@link StringResponse} with block hash.
     */
    @Override
    public StringResponse getBlockHash(Long index) {
        List<Object> params = newArrayList();
        params.add(index);
        return jsonRpc("getblockhash", params, StringResponse.class);
    }

    /**
     * Gets a block template.
     *
     * @param templateRequest
     * @return {@link GetBlockResponse}
     */
    @Override
    public GetBlockTemplateResponse getBlockTemplate(TemplateRequest templateRequest) {
        List<Object> params = newArrayList();
        params.add(templateRequest);
        return jsonRpc("getblocktemplate", params, GetBlockTemplateResponse.class);
    }

    /**
     * Returns the number of connections to other nodes.
     * 
     * @return {@link IntegerResponse} with number of connections.
     */
    @Override
    public IntegerResponse getConnectionCount() {
        return jsonRpc("getconnectioncount", EMPTY_LIST, IntegerResponse.class);
    }

    /**
     * Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
     * 
     * @return {@link LongResponse} with difficulty.
     */
    @Override
    public IntegerResponse getDifficulty() {
        return jsonRpc("getdifficulty", EMPTY_LIST, IntegerResponse.class);
    }

    /**
     * Returns true or false whether bitcoind is currently generating hashes.
     * 
     * @return {@link BooleanResponse}, true if generating.
     */
    @Override
    public BooleanResponse getGenerate() {
        return jsonRpc("getgenerate", EMPTY_LIST, BooleanResponse.class);
    }

    /**
     * Returns a recent hashes per second performance measurement while generating.
     * 
     * @return {@link LongResponse} with hashes per second.
     */
    @Override
    public LongResponse getHashesPerSecond() {
        return jsonRpc("gethashespersec", EMPTY_LIST, LongResponse.class);
    }

    /**
     * Gets various state info.
     * 
     * @return {@link GetInfoResponse}
     */
    @Override
    public GetInfoResponse getInfo() {
        return jsonRpc("getinfo", EMPTY_LIST, GetInfoResponse.class);
    }

    /**
     * Gets mining-related information.
     * 
     * @return {@link GetMiningInfoResponse} - mining-related information.
     */
    @Override
    public GetMiningInfoResponse getMiningInfo() {
        return jsonRpc("getmininginfo", EMPTY_LIST, GetMiningInfoResponse.class);
    }

    /**
     * Returns a new bitcoin address for receiving payments. If
     * <code>account</code> is specified (recommended), it is added to the
     * address book so payments received with the address will be credited to
     * <code>account</code>.
     * 
     * @param account
     *            - account to associate with the new address.
     * @return {@link StringResponse} with the new address.
     */
    @Override
    public StringResponse getNewAddress(String account) {
        List<Object> params = newArrayList();
        if (account != null)
            params.add(account);
        return jsonRpc("getnewaddress", params, StringResponse.class);
    }

    /**
     * Returns data about each connected node.
     * 
     * @return {@link GetPeerInfoResponse}
     * 
     * @since bitcoind 0.7
     */
    @Override
    public GetPeerInfoResponse getPeerInfo() {
        return jsonRpc("getpeerinfo", EMPTY_LIST, GetPeerInfoResponse.class);
    }

    /**
     * Returns all transaction ids in memory pool.
     * 
     * @return {@link StringArrayResponse} with transaction ids.
     * 
     * @since bitcoind 0.7
     */
    @Override
    public StringArrayResponse getRawMemPool() {
        return jsonRpc("getrawmempool", EMPTY_LIST, StringArrayResponse.class);
    }

    /**
     * Returns raw transaction representation for given transaction id.
     * 
     * @param txId
     *            - transaction id
     * @return {@link StringResponse} with hex encoded raw transaction.
     * 
     * @since bitcoind 0.7
     */
    @Override
    public StringResponse getRawTransaction(String txId) {
        List<Object> params = newArrayList();
        params.add(txId);
        return jsonRpc("getrawtransaction", params, StringResponse.class);
    }

    /**
     * Returns raw transaction representation for given transaction id.
     * 
     * @param txId
     *            - transaction id
     * @return {@link GetRawTransactionResponse}
     * 
     * @since bitcoind 0.7
     */
    @Override
    public GetRawTransactionResponse getRawTransaction_verbose(String txId) {
        List<Object> params = newArrayList();
        params.add(txId);
        params.add(1); // verbose
        return jsonRpc("getrawtransaction", params, GetRawTransactionResponse.class);
    }

    /**
     * Returns the total amount received by addresses with <code>account</code>
     * in transactions with at least <code>minconf</code> confirmations.
     * 
     * @param account
     * @param minConf
     *            - optional, default 1
     * @return {@link BigDecimalResponse}
     * 
     * @since bitcoind 0.3.24
     */
    @Override
    public BigDecimalResponse getReceivedByAccount(String account, Integer minConf) {
        List<Object> params = newArrayList();
        params.add(account == null ? "" : account);
        params.add(firstNotNull(minConf, 1));
        return jsonRpc("getreceivedbyaccount", params, BigDecimalResponse.class);
    }

    /**
     * Returns the total amount received by the given address in transactions
     * with at least <code>minconf</code> confirmations. While some might
     * consider this obvious, value reported by this only considers
     * <b>receiving</b> transactions. It does not check payments that have been
     * made <b>from</b> this address. In other words, this is not
     * "getAddressBalance". Works only for addresses in the local wallet,
     * external addresses will always show 0.
     * 
     * @param address
     *            - bitcoin address
     * @param minConf
     *            - optional, default 1
     * @return {@link BigDecimalResponse}
     */
    @Override
    public BigDecimalResponse getReceivedByAddress(String address, Integer minConf) {
        List<Object> params = newArrayList();
        params.add(address == null ? "" : address);
        params.add(firstNotNull(minConf, 1));
        return jsonRpc("getreceivedbyaddress", params, BigDecimalResponse.class);
    }

    /**
     * Gets data regarding the transaction with the given id.
     * 
     * @param txId - transaction id
     * @return {@link GetTransactionResponse}
     */
    @Override
    public GetTransactionResponse getTransaction(String txId) {
        List<String> params = newArrayList();
        params.add(txId);
        return jsonRpc("gettransaction", params, GetTransactionResponse.class);
    }

    /**
     * Returns details about an unspent transaction output.
     * 
     * @param txId
     *            - transaction id
     * @param n
     *            - output number
     * @param includeMemoryPool
     *            - optional, default true.
     * @return {@link GetTxOutResponse}
     */
    @Override
    public GetTxOutResponse getTxOut(String txId, Integer n, Boolean includeMemoryPool) {
        List<Object> params = newArrayList();
        params.add(txId);
        params.add(n);
        params.add(firstNotNull(includeMemoryPool, true));
        return jsonRpc("gettxout", params, GetTxOutResponse.class);
    }

    /**
     * Returns statistics about the unspent transaction output set.
     * 
     * @return
     */
    @Override
    public GetTxOutSetInfoResponse getTxOutSetInfo() {
        return jsonRpc("gettxoutsetinfo", EMPTY_LIST, GetTxOutSetInfoResponse.class);
    }

    /**
     * Returns formatted hash data to work on.
     * 
     * @return {@link GetWorkResponse} - true if succesfull.
     */
    @Override
    public GetWorkResponse getWork() {
        return jsonRpc("getwork", EMPTY_LIST, GetWorkResponse.class);
    }

    /**
     * Tries to solve the block.
     * 
     * @param data
     *            - block data
     * @return {@link BooleanResponse} - true if succesfull.
     */
    @Override
    public BooleanResponse getWork(String data) {
        List<Object> params = newArrayList();
        params.add(data);
        return jsonRpc("getwork", params, BooleanResponse.class);
    }

    /**
     * Gets help for a command or lists commands.
     * 
     * @param command - optional. If null a list of available commands is returned.
     * @return help for the given command or list of commands.
     */
    @Override
    public StringResponse help(String command) {
        List<Object> params = newArrayList();
        if (command != null)
            params.add(command);
        return jsonRpc("help", params, StringResponse.class);
    }

    /**
     * Adds a private key (as returned by dumpPrivKey) to the wallet. This may
     * take a while, as a rescan is done, looking for existing transactions.
     * Optional [rescan] parameter added in 0.8.0.
     * <p>
     * Requires unlocked wallet.
     * 
     * @param key
     * @param label
     *            - optional label
     * @param rescan
     *            - optional, default true.
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse importPrivateKey(String key, String label, Boolean rescan) {
        List<Object> params = newArrayList();
        params.add(key);
        params.add(firstNotNull(label, ""));
        params.add(firstNotNull(rescan, true));
        return jsonRpc("importprivkey", params, VoidResponse.class);
    }

    /**
     * Fills the keypool.
     * <p>
     * Requires unlocked wallet.
     * 
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse keyPoolRefill() {
        return jsonRpc("keypoolrefill", EMPTY_LIST, VoidResponse.class);
    }

    /**
     * Returns account names and balances.
     * 
     * @param minConf
     *            - minimum number of confirmations for included transactions,
     *            default 1.
     * @return {@link ListAccountsResponse}
     */
    @Override
    public ListAccountsResponse listAccounts(Integer minConf) {
        List<Object> params = newArrayList();
        params.add(firstNotNull(minConf, 1));
        return jsonRpc("listaccounts", params, ListAccountsResponse.class);
    }

    /**
     * Lists groups of addresses which have had their common ownership made
     * public by common use as inputs or as the resulting change in past
     * transactions.
     * 
     * @return {@link ListAddressGroupingsResponse}
     * 
     * @since bitcoind 0.7
     */
    @Override
    public ListAddressGroupingsResponse listAddressGroupings() {
        return jsonRpc("listaddressgroupings", EMPTY_LIST, ListAddressGroupingsResponse.class);
    }

    /**
     * Returns list of temporarily unspendable outputs.
     * 
     * @return {@link ListLockUnspentResponse}
     * 
     * @since bitcoind 0.8
     */
    @Override
    public ListLockUnspentResponse listLockUnspent() {
        return jsonRpc("listlockunspent", EMPTY_LIST, ListLockUnspentResponse.class);
    }

    /**
     * Gets amount received for each account.
     *
     * @param minConf - optional, default 1.
     * @param includeEmpty - optional, default false.
     * @return {@link ListReceivedByAccountResponse}
     */
    @Override
    public ListReceivedByAccountResponse listReceivedByAccount(Integer minConf, Boolean includeEmpty) {
        List<Object> params = newArrayList();
        params.add(firstNotNull(minConf), Integer.valueOf(1));
        params.add(firstNotNull(includeEmpty, FALSE));
        return jsonRpc("listreceivedbyaccount", params, ListReceivedByAccountResponse.class);
    }

    /**
     * Gets amount received for each address.
     * <p>
     * To get a list of accounts on the system call with minConf = 0 and includeEmpty = true.
     *
     * @param minConf - optional, default 1.
     * @param includeEmpty - optional, default false.
     * @return {@link ListReceivedByAddressResponse}
     */
    @Override
    public ListReceivedByAddressResponse listReceivedByAddress(Integer minConf, Boolean includeEmpty) {
        List<Object> params = newArrayList();
        params.add(firstNotNull(minConf), Integer.valueOf(1));
        params.add(firstNotNull(includeEmpty, FALSE));
        return jsonRpc("listreceivedbyaddress", params, ListReceivedByAddressResponse.class);
    }

    /**
     * Gets all transactions in blocks since block <code>blockhash</code>, or
     * all transactions if omitted.
     * 
     * @param blockHash - optional (may be null)
     * @param targetConfirmations - optional (may be null)
     * @return {@link ListSinceBlockResponse}
     */
    ListSinceBlockResponse listSinceBlock(String blockHash, Integer targetConfirmations) {
        List<Object> params = newArrayList();
        if (blockHash != null || targetConfirmations != null)
            params.add(blockHash);
        if (targetConfirmations != null)
            params.add(targetConfirmations);
        return jsonRpc("listsinceblock", params, ListSinceBlockResponse.class);
    }

    /**
     * Returns up to <code>count</code> most recent transactions skipping the
     * first <code>from</code> transactions for account <code>account</code>.
     * 
     * @param account
     *            - optional (may be null). If not provided will return recent
     *            transaction from all accounts.
     * @param count
     *            - optional (may be null). Maximum number of transaction to
     *            return. Default 10.
     * @param from
     *            - optional (may be null). Number of transactions to skip.
     *            Default 0.
     * @return {@link ListTransactionsResponse}
     */
    @Override
    public ListTransactionsResponse listTransactions(String account, Integer count, Integer from) {
        List<Object> params = newArrayList();
        params.add(account);
        params.add(firstNotNull(count, 10));
        params.add(firstNotNull(from, 0));
        return jsonRpc("listtransactions", params, ListTransactionsResponse.class);
    }

    /**
     * Lists unspent transaction outputs with between minConf and maxConf
     * (inclusive) confirmations. Optionally filtered to only include transaction
     * outputs paid to specified addresses.<br>
     * 
     * @param minConf
     *            - optional minimum number of confirmations. Default 1.
     * @param maxConf
     *            - optional maximum number of confirmations. Default 999999.
     * @param address
     *            - optional address(es) limiting the output to transaction
     *            outputs paid to those addresses.
     * 
     * @return {@link ListUnspentResponse}
     */
    @Override
    public ListUnspentResponse listUnspent(Integer minConf, Integer maxConf, String... address) {
        List<Object> params = newArrayList();
        params.add(firstNotNull(minConf, Integer.valueOf(1)));
        params.add(firstNotNull(maxConf, Integer.valueOf(999999)));
        params.add(address);
        return jsonRpc("listunspent", params, ListUnspentResponse.class);
    }

    /**
     * Updates list of temporarily unspendable outputs.
     * 
     * @param unlock - unlock (true) or lock (false)
     * @param txOutputs - references to transaction outputs to lock or unlock
     * @return {@link BooleanResponse}
     * 
     * @since bitcoind 0.8
     */
    @Override
    public BooleanResponse lockUnspent(Boolean unlock, TransactionOutputRef[] txOutputs) {
        List<Object> params = newArrayList();
        params.add(unlock);
        params.add(txOutputs);
        return jsonRpc("lockunspent", params, BooleanResponse.class);
    }

    /**
     * Move from one account in your wallet to another.
     * 
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @param minConf
     *            - Optional (may be null). Minimum confirmations. Default 1.
     * @param comment
     *            - optional (may be null)
     * @return
     */
    @Override
    public BooleanResponse move(String fromAccount, String toAccount, BigDecimal amount, Integer minConf,
            String comment) {
        List<Object> params = newArrayList();
        params.add(fromAccount);
        params.add(toAccount);
        params.add(amount);
        params.add(firstNotNull(minConf, 1));
        if (comment != null)
            params.add(comment);
        return jsonRpc("move", params, BooleanResponse.class);
    }

    /**
     * Sends the given amount to the given address, ensuring the account has a
     * valid balance using <code>minconf</code> confirmations. Returns the
     * transaction id if successful.
     * <p>
     * Requires unlocked wallet.
     * 
     * @param account
     * @param address
     *            - recipient's bitcoin address
     * @param amount
     *            - bitcoins
     * @param minConf
     *            - optional (may be null). Minimum number of confirmations for
     *            consumed transaction outputs. Default 1.
     * @param comment
     *            - optional (may be null). Text for the transactions comment
     *            field
     * @param commentTo
     *            - optional (may be null). Text for the transactions to: field
     * @return String with transaction number
     */
    @Override
    public StringResponse sendFrom(String account, String address, BigDecimal amount, Integer minConf,
            String comment, String commentTo) {
        List<Object> params = newArrayList();
        params.add(account);
        params.add(address);
        params.add(amount.setScale(SCALE));
        params.add(firstNotNull(minConf, 1));
        if (comment != null || commentTo != null)
            params.add(comment);
        if (commentTo != null)
            params.add(commentTo);
        return jsonRpc("sendfrom", params, StringResponse.class);
    }

    /**
     * Sends to many recipients.
     * 
     * @param fromAccount
     * @param addressesAndAmounts
     *            - recipients and amounts
     * @param minConf
     *            - optional (may be null). Minimum number of confirmations.
     *            Default 1.
     * @param commment
     *            - optional (may be null)
     * @return {@link StringResponse} with transaction id, if successful.
     */
    @Override
    public StringResponse sendMany(String fromAccount, AddressAndAmount[] addressesAndAmounts, Integer minConf,
            String commment) {
        List<Object> params = newArrayList();
        params.add(fromAccount);
        Map<String, BigDecimal> recipients = newHashMap();
        for (AddressAndAmount aaa : addressesAndAmounts) {
            String address = aaa.getAddress();
            BigDecimal amount = aaa.getAmount();
            if (recipients.containsKey(address)) {
                amount = recipients.get(address).add(amount);
            }
            recipients.put(address, amount);
        }
        params.add(recipients);
        params.add(firstNotNull(minConf, 1));
        if (commment != null)
            params.add(commment);
        return jsonRpc("sendmany", params, StringResponse.class);
    }

    /**
     * Submits raw transaction to local node and network.
     * 
     * @param hex
     *            - transaction data (serialized, hex-encoded)
     * @return {@link StringResponse} with transaction id, if successful.
     * 
     * @since bitcoind 0.7
     */
    @Override
    public StringResponse sendRawTransaction(String hex) {
        List<Object> params = newArrayList();
        params.add(hex);
        return jsonRpc("sendrawtransaction", params, StringResponse.class);
    }

    /**
     * Sends bitcoins to the given address.
     * 
     * @param address
     *            - bitcoin address
     * @param amount
     *            - bitcoins
     * @param comment
     *            - optional (may be null). Text for the transactions comment field
     * @param commentTo
     *            - optional (may be null). Text for the transactions to: field
     * @return String with transaction number
     */
    @Override
    public StringResponse sendToAddress(String address, BigDecimal amount, String comment, String commentTo) {
        List<Object> params = newArrayList();
        params.add(address);
        params.add(amount.setScale(SCALE));
        if (comment != null || commentTo != null)
            params.add(comment);
        if (commentTo != null)
            params.add(commentTo);
        return jsonRpc("sendtoaddress", params, StringResponse.class);
    }

    /**
     * Sets the account associated with the given address. Assigning an address
     * that is already assigned to the same account will create a new address
     * associated with that account.
     * 
     * @param address
     *            - bitcoin address
     * @param account
     *            - the account to set
     * @return {@link VirtualMachineError}
     */
    @Override
    public VoidResponse setAccount(String address, String account) {
        List<Object> params = newArrayList();
        params.add(address);
        params.add(account);
        return jsonRpc("setaccount", params, VoidResponse.class);
    }

    /**
     * Turnes generation on or off.
     * 
     * @param generate - turn generation on (true) or off (false).
     * @param genProcLimit
     *            - optional (may be null). Generation is limited to
     *            <code>genProcLimit</code> processors, -1 is unlimited.
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse setGenerate(Boolean generate, Integer genProcLimit) {
        List<Object> params = newArrayList();
        params.add(generate);
        if (genProcLimit != null)
            params.add(genProcLimit);
        return jsonRpc("setgenerate", params, VoidResponse.class);
    }

    /**
     * Sets transaction fee.
     *
     * @param amount - transaction fee.
     * @return {@link BooleanResponse}
     */
    @Override
    public BooleanResponse setTxFee(BigDecimal amount) {
        List<Object> params = newArrayList();
        params.add(amount.setScale(SCALE));
        return jsonRpc("settxfee", params, BooleanResponse.class);
    }

    /**
     * Sign a message with the private key of an address.
     * <p>
     * Requires unlocked wallet.
     * 
     * @param address
     *            - bitcoin address.
     * @param message
     *            - the message to sign.
     * @return {@link StringResponse} with the signed message in the result
     *         field.
     */
    @Override
    public StringResponse signMessage(String address, String message) {
        List<Object> params = newArrayList();
        params.add(address);
        params.add(message);
        return jsonRpc("signmessage", params, StringResponse.class);
    }

    /**
     * Signs inputs for raw transaction (serialized, hex-encoded).
     * <p>
     * 
     * nReturns json object with keys: hex : raw transaction with signature(s)
     * (hex-encoded string) complete : 1 if transaction has a complete set of
     * signature (0 if not)
     * <p>
     * Requires unlocked wallet.
     * 
     * {"result":"signrawtransaction <hex string> [{\
     * "txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...]
     * [<privatekey1>,...] [sighashtype=\"ALL\"]\n
     * 
     * @param hex
     *            - raw unsigned transaction.
     * @param requiredTxOuts
     *            - optional (may be null). An array of previous transaction
     *            outputs that this transaction depends on but may not yet be in
     *            the block chain
     * @param privKeys
     *            - optional (may be null). An array of base58-encoded private
     *            keys that, if given, will be the only keys used to sign the
     *            transaction.
     * @param sigHash
     *            - optional (may be null).
     * @return
     * 
     * @since bitcoind 0.7
     */
    // TODO signrawtransaction <hexstring> [{"txid":txid,"vout":n,"scriptPubKey":hex},...] [<privatekey1>,...] Adds signatures to a raw transaction and returns the resulting raw transaction. Y/N
    // TODO Define type for requiredTxOuts
    // TODO Test using args 2..4
    @Override
    public SignRawTransactionResponse signRawTransaction(String hex, Object[] requiredTxOuts, String[] privKeys,
            SignatureHashAlgorithm sigHash) {
        List<Object> params = newArrayList();
        params.add(hex);
        params.add(requiredTxOuts);
        params.add(privKeys);
        params.add(sigHash == null ? null : sigHash.toString());
        return jsonRpc("signrawtransaction", params, SignRawTransactionResponse.class);
    }

    /**
     * Stop bitcoin server.
     *
     * @return
     */
    @Override
    public VoidResponse stop() {
        return jsonRpc("stop", EMPTY_LIST, VoidResponse.class);
    }

    // TODO submitblock <hex data> [optional-params-obj]

    /**
     * Returns information about the given bitcoin address.
     *
     * @param address
     * @return {@link ValidateAddressResponse}
     */
    @Override
    public ValidateAddressResponse validateAddress(String address) {
        List<Object> params = newArrayList();
        params.add(address);
        return jsonRpc("validateaddress", params, ValidateAddressResponse.class);
    }

    /**
     * Verifies a signed message.
     * 
     * @param address
     * @param signature
     * @param message
     * @return {@link BooleanResponse}
     */
    @Override
    public BooleanResponse verifyMessage(String address, String signature, String message) {
        List<Object> params = newArrayList();
        params.add(address);
        params.add(signature);
        params.add(message);
        return jsonRpc("verifymessage", params, BooleanResponse.class);
    }

    /**
     * Removes the wallet encryption key from memory, locking the wallet.
     * <p>
     * After calling this method, you will need to call walletPassPhrase
     * again before being able to call any methods which require the wallet
     * to be unlocked.
     * 
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse walletLock() {
        return jsonRpc("walletlock", EMPTY_LIST, VoidResponse.class);
    }

    /**
     * Unlocks the wallet for the number of seconds given.
     * <p>
     * Stores the wallet decryption key in memory for <code>timeout</code>
     * seconds.
     * 
     * @param passPhrase
     * @param timeout
     *            number of seconds the encryption key is stored in memory,
     *            keeping the wallet unlocked.
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse walletPassPhrase(String passPhrase, int timeout) {
        List<Object> params = newArrayList();
        params.add(passPhrase);
        params.add(Integer.valueOf(timeout));
        return jsonRpc("walletpassphrase", params, VoidResponse.class);
    }

    /**
     * Changes the wallet passphrase from <code>oldpassphrase</code> to
     * <code>newpassphrase</code>.
     * 
     * @param oldPassPhrase
     * @param newPassPhrase
     * @return {@link VoidResponse}
     */
    @Override
    public VoidResponse walletPassPhraseChange(String oldPassPhrase, String newPassPhrase) {
        List<String> params = newArrayList();
        params.add(oldPassPhrase);
        params.add(newPassPhrase);
        return jsonRpc("walletpassphrasechange", params, VoidResponse.class);
    }

    /**
     * Performs a JSON-RPC call specifying the given method and parameters and
     * returning a response of the given type.
     * 
     * @param method
     * @param params
     * @param responseType
     * @return json response converted to the given type
     */
    private <T> T jsonRpc(String method, List<?> params, Class<T> responseType) {
        BitcoindJsonRpcRequest request = new BitcoindJsonRpcRequest(method, params);
        return restTemplate.postForObject(url, request, responseType);
    }

}