bitbank 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.autotest +23 -0
  2. data/.document +5 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +23 -0
  5. data/Gemfile.lock +59 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +60 -0
  8. data/Rakefile +42 -0
  9. data/VERSION +1 -0
  10. data/bitbank.gemspec +112 -0
  11. data/config.yml +4 -0
  12. data/lib/bitbank.rb +47 -0
  13. data/lib/bitbank/account.rb +36 -0
  14. data/lib/bitbank/client.rb +58 -0
  15. data/lib/bitbank/transaction.rb +43 -0
  16. data/spec/account_spec.rb +64 -0
  17. data/spec/bitbank_spec.rb +37 -0
  18. data/spec/client_spec.rb +134 -0
  19. data/spec/fixtures/vcr_cassettes/account/address.yml +30 -0
  20. data/spec/fixtures/vcr_cassettes/account/balance.yml +30 -0
  21. data/spec/fixtures/vcr_cassettes/account/new_address.yml +30 -0
  22. data/spec/fixtures/vcr_cassettes/account/pay.yml +60 -0
  23. data/spec/fixtures/vcr_cassettes/account/transactions.yml +30 -0
  24. data/spec/fixtures/vcr_cassettes/client/accounts.yml +30 -0
  25. data/spec/fixtures/vcr_cassettes/client/balance.yml +30 -0
  26. data/spec/fixtures/vcr_cassettes/client/balance_account.yml +30 -0
  27. data/spec/fixtures/vcr_cassettes/client/block_count.yml +30 -0
  28. data/spec/fixtures/vcr_cassettes/client/difficulty.yml +30 -0
  29. data/spec/fixtures/vcr_cassettes/client/get_work.yml +30 -0
  30. data/spec/fixtures/vcr_cassettes/client/info.yml +30 -0
  31. data/spec/fixtures/vcr_cassettes/client/new_address.yml +30 -0
  32. data/spec/fixtures/vcr_cassettes/client/transactions.yml +30 -0
  33. data/spec/fixtures/vcr_cassettes/client/transactions_account.yml +30 -0
  34. data/spec/fixtures/vcr_cassettes/transaction/details.yml +30 -0
  35. data/spec/spec_helper.rb +22 -0
  36. data/spec/support/focused.rb +7 -0
  37. data/spec/transaction_spec.rb +73 -0
  38. 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
@@ -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'