Bitcoin Wallet API v0.4.0
PHP library which facilitates communication with the Bitcoin JSON-RPC API. The rest of this documentation refers
to the Bitcoin wallet, but this library works with any Bitcoin wallet descendant. Including wallets for Litecoin and
Dogecoin., (*1)
Overview
The goal of this project is to provide more than a thin PHP wrapper to the Bitcoin JSON-RPC API; there are plenty
of PHP libraries for that purpose. This library is meant to be more powerful than other libraries by offering the
following features:, (*2)
- Concrete methods are defined for each API call, which means modern IDEs can provide auto-complete and argument documentation.
- Arguments for each API call are checked for correct type and format, which is useful during development and debugging.
- Abstracts away some of the Bitcoin API complications and inconsistencies, while tyring to stay close to the original.
- Better error handling and reporting.
- RPCPool management so that wallets may be clustered, with queries are evenly distributed to the cluster.
- Follows PSR standards.
- Designed from the ground up to be unit testable.
- Solid documentation for each API call, which is often taken directly from the Bitcoin source code.
See the Bitcoin API wiki for information on each method., (*3)
Requirements
Installing
Add the project to your composer.json as a dependency., (*4)
"require": {
"headzoo/bitcoin-wallet-api" : "dev-master"
}
And then run composer update
., (*5)
Wallet Configuration
You will need to configure you wallet to act as a server before using this library. This is done by adding a few
configuration values to the bitcoin.conf
file. Shut down your wallet if it's running, and find your Bitcoin data
directory. By default the data directory is located at /home/[user]/.bitcoin
on Linux systems,
and /Users/[user]/AppData/Roaming/Bitcoin
on Windows systems. Create the bitcoin.conf
file in the data directory
if it does not already exist., (*6)
Add the following lines:, (*7)
rpcuser=testuser
rpcpassword=testpass
rpcallowip=127.0.0.1
rpcport=9335
server=1
You will of course want to choose a strong username and password combination. Non-Bitcoin wallets are configured in the
same way. For example the Litecoin data directory is located at /home/[user]/.litecoin
on Linux systems,
and /Users/[user]/AppData/Roaming/Litecoin
on Windows systems, and the configuration file is named litecoin.conf., (*8)
Note: You will also need to add txindex=1
to your configuration if you want to query the wallet for non-wallet
transactions. You may need to start your wallet with the -rescan
switch for the first time after adding this
configuration directive., (*9)
Quick Start
<?php
use Headzoo\Bitcoin\Wallet\Api\JsonRPC;
use Headzoo\Bitcoin\Wallet\Api\Wallet;
use Headzoo\Bitcoin\Wallet\Api\RPCException;
// These configuration settings must match those from the bitcoin.conf file.
$conf = [
"user" => "testuser",
"pass" => "testpass",
"host" => "127.0.0.1",
"port" => 9332
];
// Begin by creating a Wallet instance, which needs an instance of JsonRPC passed to it's constructor.
$wallet = new Wallet(new JsonRPC($conf));
try {
// Get some basic information from the wallet.
$info = $wallet->getInfo();
print_r($info);
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
// Example output:
// [
// "version" => 90000,
// "protocolversion" => 70002,
// "walletversion" => 60000,
// "balance" => 6.02730425,
// "blocks" => 292075,
// "timeoffset" => -1,
// "connections" => 65,
// "proxy" => "",
// "difficulty" => 4250217919.86953540,
// "testnet" => false,
// "keypoololdest" => 1387569300,
// "keypoolsize" => 101,
// "paytxfee" => 0,
// "mininput" => 0.00100000,
// "unlocked_until" => 0,
// "errors" => ""
// ]
try {
// Get information about a specific block from the block chain.
$block = $wallet->getBlock("00000000000000005242ff2ddc9a407d67632ae7ee97f8c472358931b8bfc679");
print_r($block);
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
// Example output:
// [
// "hash" => "00000000000000005242ff2ddc9a407d67632ae7ee97f8c472358931b8bfc679",
// "confirmations" => 6,
// "size" => 76575,
// "height" => 292068,
// "version" => 2,
// "merkleroot" => "2520dd58da8b7e8d9f416db0c2d6669e63eb722a6bc6c344abfcfe64ac0ab024",
// "tx" => [
// "38c78866705c623615c502f13dff5da60cdfee74ec77025bbc0cd419b215bf5d",
// "46f551c0ba5822410c2349c6114dfb668adab827180eb5489a289b940e996682"
// ],
// "time" => 1395587882,
// "nonce" => 1796742204,
// "bits" => "190102b1",
// "difficulty" => 4250217919.86953540,
// "chainwork" => "000000000000000000000000000000000000000000002c6e31ad55d7fe8c8665",
// "previousblockhash" => "0000000000000000fa0424195c23ca1078d04011796f382778f211bde0a08ae5",
// "nextblockhash" => "0000000000000000c965941f8821c858c882414f0819aeccf1593076f97cb150"
// ]
// Signing a message using an address from the wallet, and then verifying the signature.
$address = "16sycWcsHDM1iedeLs11jDmryqHwsz8Bfd";
$message = "Mary had a little lamb.";
try {
// Encrypted wallets must be unlocked first.
$wallet->unlock("asd3sd945DS3a8D");
$signature = $wallet->signMessage($address, $message);
var_dump($signature);
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
// Example output:
// "IEE8F4Idkqt/q4qN4dXrMQBwrpetyrbAtPYptw+PM8As+XhSjo3qedsrlCccjaX7W+Gm9uXFz/MfLonwObgJkYw="
try {
$is_valid = $wallet->isSignedMessageValid($address, $signature, $message);
var_dump($is_valid);
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
// Example output:
// bool(true)
Class Documentation
The full class API documentation is available in the /docs directory., (*10)
Headzoo\Bitcoin\Wallet\Api\JsonRPC
Core class which directly communicates with Bitcoin wallets supporting the JSON-RPC API. This class provides a single
query($method, array $params = [])
method via the Headzoo\Bitcoin\Wallet\Api\JsonRPCInterface
interface. Although you may
use this class directly to query a wallet, it's best to use an instance of Headzoo\Bitcoin\Wallet\Api\Wallet
instead., (*11)
<?php
use Headzoo\Bitcoin\Wallet\Api\JsonRPC;
use Headzoo\Bitcoin\Wallet\Api\RPCException;
$conf = [
"user" => "testuser",
"pass" => "testpass",
"host" => "127.0.0.1",
"port" => 9332
];
$rpc = new JsonRPC($conf);
try {
$info = $rpc->query("getinfo");
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
Headzoo\Bitcoin\Wallet\Api\Wallet
Wraps an instance of Headzoo\Bitcoin\Wallet\Api\JsonRPCInterface
to provide a higher level interface to the wallet API. This
class has methods for every single API call, eg Headzoo\Bitcoin\Wallet\Api\Wallet::getInfo()
, Headzoo\Bitcoin\Wallet\Api\Wallet::backup($destination)
,
Headzoo\Bitcoin\Wallet\Api\Wallet::getAccount($account)
, etc. Using this class instead of directly using Headzoo\Bitcoin\Wallet\Api\JsonRPC
makes it easier to catch programming errors, and allows IDEs to provide auto-complete and type hinting., (*12)
<?php
use Headzoo\Bitcoin\Wallet\Api\JsonRPC;
use Headzoo\Bitcoin\Wallet\Api\Wallet;
use Headzoo\Bitcoin\Wallet\Api\RPCException;
$conf = [
"user" => "testnet",
"pass" => "testnet",
"host" => "localhost",
"port" => 9332
];
try {
$wallet = new Wallet(JsonRPC($conf));
$info = $wallet->getInfo();
$account = $wallet->getAccount("personal");
$count = $wallet->getBlockCount();
} catch (RPCException $e) {
echo $e->getTraceAsString();
die();
}
Headzoo\Bitcoin\Wallet\Api\RPCPool
Wallet servers may be clustered and queried randomly using the Headzoo\Bitcoin\Wallet\Api\RPCPool
class.
Both Headzoo\Bitcoin\Wallet\Api\JsonRPC
and Headzoo\Bitcoin\Wallet\Api\RPCPool
implement Headzoo\Bitcoin\Wallet\Api\JsonRPCInterface
, which
means either may be passed to the Headzoo\Bitcoin\Wallet\Api\Wallet
constructor., (*13)
<?php
use Headzoo\Bitcoin\Wallet\Api\RPCPool;
use Headzoo\Bitcoin\Wallet\Api\JsonRPC;
use Headzoo\Bitcoin\Wallet\Api\RPCException;
// Start by creating a new pool, adding JsonRPCInterface instances to it, and then pass the pool
// to the Wallet constructor.
$conf = [
"wallet1" => [
"user" => "testnet",
"pass" => "testnet",
"host" => "localhost",
"port" => 9332
],
"wallet2" => [
"user" => "testnet",
"pass" => "testnet",
"host" => "localhost",
"port" => 9333
]
];
$pool = new RPCPool();
$pool->add(new JsonRPC($conf["wallet1"]));
$pool->add(new JsonRPC($conf["wallet2"]));
$wallet = new Wallet($pool);
// A different server will be chosen by the pool for each method call.
try {
$info = $wallet->getInfo();
$balance = $wallet->getBalance();
$accounts = $wallet->getAccounts();
} catch (Headzoo\Bitcoin\Wallet\Api\RPCException $e) {
echo $e->getTraceAsString();
die();
}
Change Log
v0.4.0 - 2014/03/23
- Increased minimum PHP version to 5.5.
- Renamed project to Bitcoin Wallet API.
- Renamed namespace
Headzoo\CoinTalk
to Headzoo\Bitcoin\Wallet\Api
.
- Refactored the unit tests to use more mocks.
- Using headzoo/web-tools for making HTTP requests.
v0.3.0 - 2014/03/23
- Renamed class
Headzoo\CoinTalk\Api
to Headzoo\CoinTalk\Wallet
.
- Renamed class
Headzoo\CoinTalk\Server
to Headzoo\CoinTalk\JsonRPC
.
- Renamed class
Headzoo\CoinTalk\IServer
to Headzoo\CoinTalk\JsonRPCInterface
.
- Renamed class
Headzoo\CoinTalk\Pool
to Headzoo\CoinTalk\RPCPool
.
- Renamed methods starting with
list
in the Headzoo\CoinTalk\Wallet
class with get
, eg listAccounts()
was renamed to getAccounts()
.
- Renamed the following methods to make the method names more consistent, and so they conform to my naming standards:
-
Headzoo\CoinTalk\Wallet::sendRawTransaction()
to Headzoo\CoinTalk\Wallet::submitRawTransaction()
.
-
Headzoo\CoinTalk\Wallet::submitBlock()
to Headzoo\CoinTalk\Wallet::submitRawBlock()
.
-
Headzoo\CoinTalk\Wallet::getRawMemPool()
to Headzoo\CoinTalk\Wallet::getTransactionsFromMemoryPool()
.
-
Headzoo\CoinTalk\Wallet::dumpPrivKey()
to Headzoo\CoinTalk\Wallet::getPrivateKeyByAddress()
.
-
Headzoo\CoinTalk\Wallet::importPrivKey()
to Headzoo\CoinTalk\Wallet::addPrivateKey()
.
-
Headzoo\CoinTalk\Wallet::lockUnspent()
to Headzoo\CoinTalk\Wallet::setLockUnspent()
.
-
Headzoo\CoinTalk\Wallet::sendToAddress()
to Headzoo\CoinTalk\Wallet::send()
.
-
Headzoo\CoinTalk\Wallet::sendFrom()
to Headzoo\CoinTalk\Wallet::sendFromAccount()
.
-
Headzoo\CoinTalk\Wallet::sendMany()
to Headzoo\CoinTalk\Wallet::sendManyFromAccount()
.
-
Headzoo\CoinTalk\Wallet::verifyMessage()
to Headzoo\CoinTalk\Wallet::isSignedMessageValid()
.
-
Headzoo\CoinTalk\Wallet::validateAddress()
to Headzoo\CoinTalk\Wallet::getAddressInfo()
.
-
Headzoo\CoinTalk\Wallet::encryptWallet()
to Headzoo\CoinTalk\Wallet::encrypt()
.
-
Headzoo\CoinTalk\Wallet::walletLock()
to Headzoo\CoinTalk\Wallet::lock()
.
-
Headzoo\CoinTalk\Wallet::walletPassPhrase()
to Headzoo\CoinTalk\Wallet::unlock()
.
-
Headzoo\CoinTalk\Wallet::walletPassPhraseChange()
to Headzoo\CoinTalk\Wallet::changePassPhrase()
.
-
Headzoo\CoinTalk\Wallet::keyPoolRefill()
to Headzoo\CoinTalk\Wallet::fillKeyPool()
.
-
Headzoo\CoinTalk\Wallet::stop()
to Headzoo\CoinTalk\Wallet::stop()
.
-
Headzoo\CoinTalk\Wallet::setTxFee()
to Headzoo\CoinTalk\Wallet::setTransactionFee()
.
-
Headzoo\CoinTalk\Wallet::getReceivedByAddress()
to Headzoo\CoinTalk\Wallet::getBalanceByAddress()
.
-
Headzoo\CoinTalk\Wallet::getAccount()
to Headzoo\CoinTalk\Wallet::getAccountByAddress()
.
-
Headzoo\CoinTalk\Wallet::getAccountAddress()
to Headzoo\CoinTalk\Wallet::getAddressByAccount()
.
-
Headzoo\CoinTalk\Wallet::createMultiSig()
to Headzoo\CoinTalk\Wallet::getNewMultiSignatureAddress()
.
-
Headzoo\CoinTalk\Wallet::addMultiSigAddress()
to Headzoo\CoinTalk\Wallet::addMultiSignatureAddress()
.
-
Headzoo\CoinTalk\Wallet::getTxOut()
to Headzoo\CoinTalk\Wallet::getTransactionOut()
.
-
Headzoo\CoinTalk\Wallet::getTxOutSetInfo()
to Headzoo\CoinTalk\Wallet::getTransactionOutSet()
.
-
Headzoo\CoinTalk\Wallet::getAddedNodeInfo()
to Headzoo\CoinTalk\Wallet::getNodeInfo()
.
- Removed the following methods:
-
Headzoo\CoinTalk\Wallet::getReceivedByAccount()
.
-
Headzoo\CoinTalk\Wallet::listReceivedByAccount()
.
v0.2.0 - 2013/12/31
v0.1.0 - 2013/12/18
TODO
- Ensure the wallet server version supports specific calls.
- Document which methods need an unlocked wallet.
- Create classes which represent:
- Blocks
- Keys (public/private)
- Transactions
- etc
License
This content is released under the MIT License. See the included LICENSE for more information., (*14)
I write code because I like writing code, and writing code is a reward in itself, but donations are always welcome., (*15)
Bitcoin: 1Headz2mYtpBRo6KFaaUEtcm5Kce6BZRJM
Litecoin: LheadzBgTNAitxYxUTUTTQ3RT7zR5jnkfq, (*16)