bitbank 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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'
|