bitbank 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +60 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/bitbank.gemspec +112 -0
- data/config.yml +4 -0
- data/lib/bitbank.rb +47 -0
- data/lib/bitbank/account.rb +36 -0
- data/lib/bitbank/client.rb +58 -0
- data/lib/bitbank/transaction.rb +43 -0
- data/spec/account_spec.rb +64 -0
- data/spec/bitbank_spec.rb +37 -0
- data/spec/client_spec.rb +134 -0
- data/spec/fixtures/vcr_cassettes/account/address.yml +30 -0
- data/spec/fixtures/vcr_cassettes/account/balance.yml +30 -0
- data/spec/fixtures/vcr_cassettes/account/new_address.yml +30 -0
- data/spec/fixtures/vcr_cassettes/account/pay.yml +60 -0
- data/spec/fixtures/vcr_cassettes/account/transactions.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/accounts.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/balance.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/balance_account.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/block_count.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/difficulty.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/get_work.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/info.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/new_address.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/transactions.yml +30 -0
- data/spec/fixtures/vcr_cassettes/client/transactions_account.yml +30 -0
- data/spec/fixtures/vcr_cassettes/transaction/details.yml +30 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/focused.rb +7 -0
- data/spec/transaction_spec.rb +73 -0
- metadata +232 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Bitbank
|
2
|
+
class Client
|
3
|
+
def initialize(config={})
|
4
|
+
@config = config
|
5
|
+
@endpoint = "http://#{config[:username]}:#{config[:password]}" +
|
6
|
+
"@#{config[:host]}:#{config[:port]}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def accounts
|
10
|
+
account_data = request('listaccounts')
|
11
|
+
account_data.map do |account_name, account_value|
|
12
|
+
Account.new(self, account_name, account_value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def balance(account_name=nil)
|
17
|
+
request('getbalance', account_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the number of blocks in the longest block chain.
|
21
|
+
def block_count
|
22
|
+
request('getblockcount')
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
|
26
|
+
def difficulty
|
27
|
+
request('getdifficulty')
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_work(data=nil)
|
31
|
+
request('getwork', data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def info
|
35
|
+
request('getinfo')
|
36
|
+
end
|
37
|
+
|
38
|
+
def new_address(account_name=nil)
|
39
|
+
request('getnewaddress', account_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def transactions(account_name=nil, count=10)
|
43
|
+
transaction_data = request('listtransactions', account_name, count)
|
44
|
+
transaction_data.map do |txdata|
|
45
|
+
Transaction.new(self, txdata['txid'], txdata)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def request(method, *args)
|
50
|
+
body = { 'id' => 'jsonrpc', 'method' => method }
|
51
|
+
body['params'] = args unless args.empty? || args.first.nil?
|
52
|
+
|
53
|
+
response_json = RestClient.post(@endpoint, body.to_json)
|
54
|
+
response = JSON.parse(response_json)
|
55
|
+
response['result']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Bitbank
|
2
|
+
class Transaction
|
3
|
+
attr_reader :txid, :address, :category, :amount, :confirmations
|
4
|
+
|
5
|
+
def initialize(client, txid, data={})
|
6
|
+
@client = client
|
7
|
+
@txid = txid
|
8
|
+
|
9
|
+
load_details(data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def account
|
13
|
+
@account ? Account.new(@client, @account) : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def time
|
17
|
+
Time.at(@time)
|
18
|
+
end
|
19
|
+
|
20
|
+
def confirmed?
|
21
|
+
confirmations && confirmations > 6
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
txid == other.txid
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def load_details(data={})
|
31
|
+
data = @client.request('gettransaction', txid) if data.empty?
|
32
|
+
data.symbolize_keys!
|
33
|
+
|
34
|
+
details = ((data.delete(:details) || []).first || {}).symbolize_keys
|
35
|
+
@account = data[:account] || details[:account]
|
36
|
+
@address = data[:address] || details[:address]
|
37
|
+
@category = data[:category] || details[:category]
|
38
|
+
@amount = data[:amount] || details[:amount]
|
39
|
+
@confirmations = data[:confirmations]
|
40
|
+
@time = data[:time]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Bitbank::Account" do
|
4
|
+
before(:each) do
|
5
|
+
@client = Bitbank.new(File.join(File.dirname(__FILE__), '..', 'config.yml'))
|
6
|
+
@account = Bitbank::Account.new(@client, 'adent', 0.02)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should have a name' do
|
10
|
+
@account.name.should == 'adent'
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'address' do
|
14
|
+
use_vcr_cassette 'account/address'
|
15
|
+
|
16
|
+
it 'should retrieve the address for this account' do
|
17
|
+
@account.address.should == '1NqwGDRi9Gs4xm1BmPnGeMwgz1CowP6CeQ'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'balance' do
|
22
|
+
use_vcr_cassette 'account/balance'
|
23
|
+
|
24
|
+
it 'should retrieve the current balance' do
|
25
|
+
@account.balance.should == 0.02
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'new_address' do
|
30
|
+
use_vcr_cassette 'account/new_address'
|
31
|
+
|
32
|
+
it 'should create a new address associated with this account and return it' do
|
33
|
+
@account.new_address.should == '15GsE7o3isyQ7ygzh8Cya58oetrGYygdoi'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'pay' do
|
38
|
+
use_vcr_cassette 'account/pay'
|
39
|
+
|
40
|
+
it 'should return a new transaction' do
|
41
|
+
transaction = @account.pay('destinationaddress', 0.01)
|
42
|
+
transaction.amount.should == 0.01
|
43
|
+
transaction.account.should == @account
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'transactions' do
|
48
|
+
use_vcr_cassette 'account/transactions'
|
49
|
+
|
50
|
+
it 'should retrieve transactions for this account' do
|
51
|
+
transactions = @account.transactions
|
52
|
+
transactions.length.should == 1
|
53
|
+
transactions.first.is_a?(Bitbank::Transaction).should be_true
|
54
|
+
transactions.first.txid.should == 'txid1'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'equality' do
|
59
|
+
it 'should compare account names' do
|
60
|
+
@account.should_not == Bitbank::Account.new(@client, 'prefect')
|
61
|
+
@account.should == Bitbank::Account.new(@client, 'adent')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Bitbank" do
|
4
|
+
describe 'initialization' do
|
5
|
+
it 'should configure the client' do
|
6
|
+
client = Bitbank.new(:username => 'prefect', :password => 'hoopy')
|
7
|
+
client.is_a?(Bitbank::Client).should be_true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'configuration' do
|
12
|
+
it 'should require a username and password' do
|
13
|
+
expect {
|
14
|
+
Bitbank.config = { :username => 'marvin' }
|
15
|
+
}.to raise_error(ArgumentError, 'Please specify a username and password for bitcoind')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should parse a yaml file for credentials' do
|
19
|
+
Bitbank.config = File.join(File.dirname(__FILE__), '..', 'config.yml')
|
20
|
+
Bitbank.config[:username].should == 'testuser'
|
21
|
+
Bitbank.config[:password].should == 'testpass'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should use default values' do
|
25
|
+
Bitbank.config = File.join(File.dirname(__FILE__), '..', 'config.yml')
|
26
|
+
Bitbank.config[:host].should == 'localhost'
|
27
|
+
Bitbank.config[:port].should == 8332
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'version' do
|
32
|
+
it 'should read version from file' do
|
33
|
+
Bitbank.version.should == File.read(
|
34
|
+
File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Bitbank::Client" do
|
4
|
+
before(:each) do
|
5
|
+
@client = Bitbank.new(File.join(File.dirname(__FILE__), '..', 'config.yml'))
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'request' do
|
9
|
+
before(:each) do
|
10
|
+
@json = '{"result":{"foo":"bar"}}'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should post to the endpoint' do
|
14
|
+
RestClient.expects(:post).with(
|
15
|
+
@client.instance_variable_get('@endpoint'), anything).returns(@json)
|
16
|
+
@client.request('foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should parse json results' do
|
20
|
+
RestClient.stubs(:post).returns(@json)
|
21
|
+
@client.request('whatever').should == { 'foo' => 'bar' }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'accounts' do
|
26
|
+
use_vcr_cassette 'client/accounts'
|
27
|
+
|
28
|
+
it 'should retrieve all accounts' do
|
29
|
+
accounts = @client.accounts
|
30
|
+
accounts.length.should == 3
|
31
|
+
accounts.last.name.should == 'misc'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'balance' do
|
36
|
+
context 'overall' do
|
37
|
+
use_vcr_cassette 'client/balance'
|
38
|
+
|
39
|
+
it 'should retreive the current balance' do
|
40
|
+
@client.balance.should == 12.34
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'scoped to a particular account' do
|
45
|
+
use_vcr_cassette 'client/balance_account'
|
46
|
+
|
47
|
+
it 'should retrieve the account balance' do
|
48
|
+
@client.balance.should == 10.05
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'block_count' do
|
54
|
+
use_vcr_cassette 'client/block_count'
|
55
|
+
|
56
|
+
it 'should return the number of blocks in the longest block chain' do
|
57
|
+
@client.block_count.should == 130361
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'difficulty' do
|
62
|
+
use_vcr_cassette 'client/difficulty'
|
63
|
+
|
64
|
+
it 'should return the current difficulty' do
|
65
|
+
@client.difficulty.should == 567358.22457067
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'get_work' do
|
70
|
+
context 'when data is not supplied' do
|
71
|
+
use_vcr_cassette 'client/get_work'
|
72
|
+
|
73
|
+
it 'should return new formatted hash data to work on' do
|
74
|
+
work = @client.get_work
|
75
|
+
['midstate', 'data', 'hash1', 'target'].each do |key|
|
76
|
+
work.has_key?(key).should be_true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when data is supplied' do
|
82
|
+
it 'checks my answer'
|
83
|
+
it 'gives me more work'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'info' do
|
88
|
+
use_vcr_cassette 'client/info'
|
89
|
+
|
90
|
+
it 'should return a hash containing bitcoind status information' do
|
91
|
+
@result = @client.info
|
92
|
+
|
93
|
+
@result['version'].should == 32100
|
94
|
+
@result['testnet'].should be_false
|
95
|
+
|
96
|
+
info_keys = ['version', 'balance', 'blocks', 'connections', 'proxy',
|
97
|
+
'generate', 'genproclimit', 'difficulty', 'hashespersec', 'testnet',
|
98
|
+
'keypoololdest', 'paytxfee', 'errors']
|
99
|
+
info_keys.each do |key|
|
100
|
+
@result.keys.include?(key).should be_true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'new_address' do
|
106
|
+
use_vcr_cassette 'client/new_address', :record => :new_episodes
|
107
|
+
|
108
|
+
it 'should create a new address and return it' do
|
109
|
+
@client.new_address.should == '1EzxbYD4rFvZBjUEbtnKZ9KJdrqHB7mkZE'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'transactions' do
|
114
|
+
context 'overall' do
|
115
|
+
use_vcr_cassette 'client/transactions'
|
116
|
+
|
117
|
+
it 'should retrieve all transactions' do
|
118
|
+
transactions = @client.transactions
|
119
|
+
transactions.length.should == 4
|
120
|
+
transactions.last.txid.should == 'txid4'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'scoped to a particular account' do
|
125
|
+
use_vcr_cassette 'client/transactions_account'
|
126
|
+
|
127
|
+
it 'should retrieve transactions for the account' do
|
128
|
+
transactions = @client.transactions('adent')
|
129
|
+
transactions.length.should == 1
|
130
|
+
transactions.last.txid.should == 'txid1'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :post
|
5
|
+
uri: http://testuser:testpass@localhost:8332/
|
6
|
+
body: ! '{"id":"jsonrpc","method":"getaccountaddress","params":["prefect"]}'
|
7
|
+
headers:
|
8
|
+
accept:
|
9
|
+
- ! '*/*; q=0.5, application/xml'
|
10
|
+
accept-encoding:
|
11
|
+
- gzip, deflate
|
12
|
+
content-length:
|
13
|
+
- '61'
|
14
|
+
response: !ruby/struct:VCR::Response
|
15
|
+
status: !ruby/struct:VCR::ResponseStatus
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
date:
|
20
|
+
- Sun, 12 Jun 2011 03:38:37 +0000
|
21
|
+
content-length:
|
22
|
+
- '76'
|
23
|
+
content-type:
|
24
|
+
- application/json
|
25
|
+
server:
|
26
|
+
- bitcoin-json-rpc/0.3.21-beta
|
27
|
+
body: ! '{"result":"1NqwGDRi9Gs4xm1BmPnGeMwgz1CowP6CeQ","error":null,"id":"jsonrpc"}
|
28
|
+
|
29
|
+
'
|
30
|
+
http_version: '1.1'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :post
|
5
|
+
uri: http://testuser:testpass@localhost:8332/
|
6
|
+
body: ! '{"id":"jsonrpc","method":"getbalance","params":["adent"]}'
|
7
|
+
headers:
|
8
|
+
accept:
|
9
|
+
- ! '*/*; q=0.5, application/xml'
|
10
|
+
accept-encoding:
|
11
|
+
- gzip, deflate
|
12
|
+
content-length:
|
13
|
+
- '54'
|
14
|
+
response: !ruby/struct:VCR::Response
|
15
|
+
status: !ruby/struct:VCR::ResponseStatus
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
date:
|
20
|
+
- Sun, 12 Jun 2011 03:36:43 +0000
|
21
|
+
content-length:
|
22
|
+
- '50'
|
23
|
+
content-type:
|
24
|
+
- application/json
|
25
|
+
server:
|
26
|
+
- bitcoin-json-rpc/0.3.21-beta
|
27
|
+
body: ! '{"result":0.02,"error":null,"id":"jsonrpc"}
|
28
|
+
|
29
|
+
'
|
30
|
+
http_version: '1.1'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :post
|
5
|
+
uri: http://testuser:testpass@localhost:8332/
|
6
|
+
body: ! '{"id":"jsonrpc","method":"getnewaddress","params":["adent"]}'
|
7
|
+
headers:
|
8
|
+
accept:
|
9
|
+
- ! '*/*; q=0.5, application/xml'
|
10
|
+
accept-encoding:
|
11
|
+
- gzip, deflate
|
12
|
+
content-length:
|
13
|
+
- '60'
|
14
|
+
response: !ruby/struct:VCR::Response
|
15
|
+
status: !ruby/struct:VCR::ResponseStatus
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
date:
|
20
|
+
- Mon, 13 Jun 2011 04:40:14 +0000
|
21
|
+
content-length:
|
22
|
+
- '76'
|
23
|
+
content-type:
|
24
|
+
- application/json
|
25
|
+
server:
|
26
|
+
- bitcoin-json-rpc/0.3.21-beta
|
27
|
+
body: ! '{"result":"15GsE7o3isyQ7ygzh8Cya58oetrGYygdoi","error":null,"id":"jsonrpc"}
|
28
|
+
|
29
|
+
'
|
30
|
+
http_version: '1.1'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
- !ruby/struct:VCR::HTTPInteraction
|
3
|
+
request: !ruby/struct:VCR::Request
|
4
|
+
method: :post
|
5
|
+
uri: http://testuser:testpass@localhost:8332/
|
6
|
+
body: ! '{"id":"jsonrpc","method":"sendfrom","params":["adent","destinationaddress",0.1]}'
|
7
|
+
headers:
|
8
|
+
accept:
|
9
|
+
- ! '*/*; q=0.5, application/xml'
|
10
|
+
accept-encoding:
|
11
|
+
- gzip, deflate
|
12
|
+
content-length:
|
13
|
+
- '107'
|
14
|
+
response: !ruby/struct:VCR::Response
|
15
|
+
status: !ruby/struct:VCR::ResponseStatus
|
16
|
+
code: 200
|
17
|
+
message: OK
|
18
|
+
headers:
|
19
|
+
date:
|
20
|
+
- Sun, 12 Jun 2011 03:59:59 +0000
|
21
|
+
content-length:
|
22
|
+
- '106'
|
23
|
+
content-type:
|
24
|
+
- application/json
|
25
|
+
server:
|
26
|
+
- bitcoin-json-rpc/0.3.21-beta
|
27
|
+
body: ! '{"result":"newtxid","error":null,"id":"jsonrpc"}
|
28
|
+
|
29
|
+
'
|
30
|
+
http_version: '1.1'
|
31
|
+
|
32
|
+
- !ruby/struct:VCR::HTTPInteraction
|
33
|
+
request: !ruby/struct:VCR::Request
|
34
|
+
method: :post
|
35
|
+
uri: http://testuser:testpass@localhost:8332/
|
36
|
+
body: ! '{"id":"jsonrpc","method":"gettransaction","params":["newtxid"]}'
|
37
|
+
headers:
|
38
|
+
accept:
|
39
|
+
- ! '*/*; q=0.5, application/xml'
|
40
|
+
accept-encoding:
|
41
|
+
- gzip, deflate
|
42
|
+
content-length:
|
43
|
+
- '120'
|
44
|
+
response: !ruby/struct:VCR::Response
|
45
|
+
status: !ruby/struct:VCR::ResponseStatus
|
46
|
+
code: 200
|
47
|
+
message: OK
|
48
|
+
headers:
|
49
|
+
date:
|
50
|
+
- Mon, 13 Jun 2011 04:00:01 +0000
|
51
|
+
content-length:
|
52
|
+
- '289'
|
53
|
+
content-type:
|
54
|
+
- application/json
|
55
|
+
server:
|
56
|
+
- bitcoin-json-rpc/0.3.21-beta
|
57
|
+
body: ! '{"result":{"amount":0.01,"confirmations":1,"txid":"newtxid","time":1307851201,"details":[{"account":"adent","address":"addr1","category":"send","amount":0.1}]},"error":null,"id":"jsonrpc"}
|
58
|
+
|
59
|
+
'
|
60
|
+
http_version: '1.1'
|