bitcoin-client 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +79 -0
- data/Rakefile +9 -0
- data/bitcoind-client.gemspec +28 -0
- data/lib/bitcoin-client.rb +1 -0
- data/lib/bitcoin.rb +19 -0
- data/lib/bitcoin/api.rb +33 -0
- data/lib/bitcoin/client.rb +227 -0
- data/lib/bitcoin/dsl.rb +259 -0
- data/lib/bitcoin/errors.rb +4 -0
- data/lib/bitcoin/request.rb +35 -0
- data/lib/bitcoin/rpc.rb +50 -0
- data/lib/bitcoin/version.rb +12 -0
- data/spec/fixtures/backupwallet_without_params.json +8 -0
- data/spec/fixtures/build_fixture.rb +19 -0
- data/spec/fixtures/getbalance.json +8 -0
- data/spec/fixtures/getblockcount.json +8 -0
- data/spec/fixtures/getblocknumber.json +8 -0
- data/spec/fixtures/getconnectioncount.json +8 -0
- data/spec/fixtures/getdifficulty.json +8 -0
- data/spec/fixtures/getgenerate.json +8 -0
- data/spec/fixtures/gethashespersec.json +8 -0
- data/spec/fixtures/getinfo.json +8 -0
- data/spec/fixtures/help.json +8 -0
- data/spec/fixtures/listreceivedbyaddress_with_minconf_0.json +8 -0
- data/spec/fixtures/listreceivedbyaddress_with_minconf_0_and_includeempty_true.json +7 -0
- data/spec/fixtures/listreceivedbyaddress_without_params.json +7 -0
- data/spec/lib/bitcoin/api_spec.rb +28 -0
- data/spec/lib/bitcoin/client_spec.rb +108 -0
- data/spec/lib/bitcoin/request_spec.rb +19 -0
- data/spec/lib/bitcoin_spec.rb +34 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/fixtures_helper.rb +5 -0
- data/spec/support/rpc_service_helper.rb +34 -0
- metadata +141 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Bitcoin::Request
|
4
|
+
attr_reader :service_name, :params
|
5
|
+
|
6
|
+
def initialize(service_name, params = [])
|
7
|
+
@service_name = service_name
|
8
|
+
@params = params.dup
|
9
|
+
|
10
|
+
# bitcoin rejects null values even for optional params. Since
|
11
|
+
# even params following those may have default non-nil values,
|
12
|
+
# we'll assume the first non-nil value marks a set of optional
|
13
|
+
# params, and drop it and everything following it.
|
14
|
+
#
|
15
|
+
# ex:
|
16
|
+
# [nil] => []
|
17
|
+
# [1,nil,nil] => [1]
|
18
|
+
# [1,nil,nil,1] => [1]
|
19
|
+
if index = @params.index(nil)
|
20
|
+
@params = @params[0...index]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash
|
25
|
+
{
|
26
|
+
:method => service_name,
|
27
|
+
:params => params,
|
28
|
+
:id => "jsonrpc"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_post_data
|
33
|
+
to_hash.to_json
|
34
|
+
end
|
35
|
+
end
|
data/lib/bitcoin/rpc.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
class Bitcoin::RPC
|
4
|
+
def initialize(options)
|
5
|
+
@user, @pass = options[:user], options[:pass]
|
6
|
+
@host, @port = options[:host], options[:port]
|
7
|
+
@ssl = options[:ssl]
|
8
|
+
end
|
9
|
+
|
10
|
+
def credentials
|
11
|
+
if @user
|
12
|
+
"#{@user}:#{@pass}"
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def service_url
|
19
|
+
url = @ssl ? "https://" : "http://"
|
20
|
+
url.concat "#{credentials}@" if c = credentials
|
21
|
+
url.concat "#{@host}:#{@port}"
|
22
|
+
url
|
23
|
+
end
|
24
|
+
|
25
|
+
def dispatch(request)
|
26
|
+
respdata = RestClient.post service_url, request.to_post_data
|
27
|
+
response = JSON.parse(respdata)
|
28
|
+
raise Bitcoin::Errors::RPCError, response['error'] if response['error']
|
29
|
+
response['result']
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def symbolize_keys(hash)
|
34
|
+
case hash
|
35
|
+
when Hash
|
36
|
+
hash.inject({}) do |result, (key, value)|
|
37
|
+
key = key.to_sym if key.kind_of?(String)
|
38
|
+
value = symbolize_keys(value)
|
39
|
+
result[key] = value
|
40
|
+
result
|
41
|
+
end
|
42
|
+
when Array
|
43
|
+
hash.collect do |ele|
|
44
|
+
symbolize_keys(ele)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
hash
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 500 Internal Server Error
|
2
|
+
Date: Sun, 21 Aug 2011 22:53:05 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Length: 183
|
5
|
+
Content-Type: application/json
|
6
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
7
|
+
|
8
|
+
{"result":null,"error":{"code":-1,"message":"backupwallet <destination>\nSafely copies wallet.dat to destination, which can be a directory or a path with filename."},"id":"curltest"}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
service_name = ARGV[0]
|
7
|
+
file_name = (ARGV[1] || service_name).dup
|
8
|
+
params = ARGV[2..-1].collect { |y| y == '_nil' ? nil : YAML::load(y) }
|
9
|
+
|
10
|
+
file_name << ".json" unless file_name =~ /\.json$/
|
11
|
+
|
12
|
+
data = { 'jsonrpc' => '1.0', 'id' => 'curltest', 'method' => service_name, 'params' => params }
|
13
|
+
command = "curl --user cm4:H2na-92D --data-binary '#{data.to_json}' -H 'content-type: text/plain;' http://127.0.0.1:8332/ -i"
|
14
|
+
|
15
|
+
puts command, nil
|
16
|
+
result = %x[#{command}]
|
17
|
+
puts result, nil
|
18
|
+
File.open(file_name, "w") { |f| f.print result }
|
19
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sun, 21 Aug 2011 20:24:03 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Length: 281
|
5
|
+
Content-Type: application/json
|
6
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
7
|
+
|
8
|
+
{"result":{"version":32400,"balance":0.00100000,"blocks":141957,"connections":8,"proxy":"","generate":false,"genproclimit":-1,"difficulty":1805700.83619367,"hashespersec":0,"testnet":false,"keypoololdest":1313766189,"paytxfee":0.00000000,"errors":""},"error":null,"id":"curltest"}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sun, 21 Aug 2011 23:20:10 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Length: 1078
|
5
|
+
Content-Type: application/json
|
6
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
7
|
+
|
8
|
+
{"result":"backupwallet <destination>\ngetaccount <bitcoinaddress>\ngetaccountaddress <account>\ngetaddressesbyaccount <account>\ngetbalance [account] [minconf=1]\ngetblockcount\ngetblocknumber\ngetconnectioncount\ngetdifficulty\ngetgenerate\ngethashespersec\ngetinfo\ngetnewaddress [account]\ngetreceivedbyaccount <account> [minconf=1]\ngetreceivedbyaddress <bitcoinaddress> [minconf=1]\ngettransaction <txid>\ngetwork [data]\nhelp [command]\nlistaccounts [minconf=1]\nlistreceivedbyaccount [minconf=1] [includeempty=false]\nlistreceivedbyaddress [minconf=1] [includeempty=false]\nlisttransactions [account] [count=10] [from=0]\nmove <fromaccount> <toaccount> <amount> [minconf=1] [comment]\nsendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\nsendmany <fromaccount> {address:amount,...} [minconf=1] [comment]\nsendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\nsetaccount <bitcoinaddress> <account>\nsetgenerate <generate> [genproclimit]\nsettxfee <amount>\nstop\nvalidateaddress <bitcoinaddress>","error":null,"id":"curltest"}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sun, 21 Aug 2011 23:04:42 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Length: 155
|
5
|
+
Content-Type: application/json
|
6
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
7
|
+
|
8
|
+
{"result":[{"address":"1234","account":"","label":"","amount":0.00100000,"confirmations":180}],"error":null,"id":"curltest"}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sun, 21 Aug 2011 23:15:11 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Type: application/json
|
5
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
6
|
+
|
7
|
+
{"result":[{"address":"1234","account":"","label":"","amount":0.00100000,"confirmations":180}],"error":null,"id":"curltest"}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sun, 21 Aug 2011 22:54:36 +0000
|
3
|
+
Connection: close
|
4
|
+
Content-Type: application/json
|
5
|
+
Server: bitcoin-json-rpc/0.3.24-beta
|
6
|
+
|
7
|
+
{"result":[{"address":"1234","account":"","label":"","amount":0.00100000,"confirmations":180}],"error":null,"id":"curltest"}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bitcoin::API do
|
4
|
+
subject { Bitcoin::API.new(:user => $user, :pass => $pass) }
|
5
|
+
|
6
|
+
it "should have default host, port, ssl" do
|
7
|
+
subject.host.should == 'localhost'
|
8
|
+
subject.port.should == 8332
|
9
|
+
subject.ssl?.should be_false
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept host, port, ssl options" do
|
13
|
+
req = Bitcoin::API.new(:user => $user, :pass => $pass, :host => 'example.com', :port => 1234, :ssl => true)
|
14
|
+
req.host.should == 'example.com'
|
15
|
+
req.port.should == 1234
|
16
|
+
req.ssl?.should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should build an options hash" do
|
20
|
+
subject.to_hash.should == {
|
21
|
+
:user => $user,
|
22
|
+
:pass => $pass,
|
23
|
+
:host => 'localhost',
|
24
|
+
:port => 8332,
|
25
|
+
:ssl => false,
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bitcoin::Client do
|
4
|
+
subject { Bitcoin::Client.new($user, $pass) }
|
5
|
+
|
6
|
+
it "defaults" do
|
7
|
+
subject.user.should == $user
|
8
|
+
subject.pass.should == $pass
|
9
|
+
subject.host.should == 'localhost'
|
10
|
+
subject.port.should == 8332
|
11
|
+
subject.ssl?.should_not be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
context "RPC" do
|
15
|
+
extend RPCServiceHelper
|
16
|
+
|
17
|
+
service 'getinfo' do
|
18
|
+
it "should produce the expected result" do
|
19
|
+
result.should == {
|
20
|
+
'version' => 32400,
|
21
|
+
'balance' => 0.001,
|
22
|
+
'blocks' => 141957,
|
23
|
+
'connections' => 8,
|
24
|
+
'proxy' => "",
|
25
|
+
'generate' => false,
|
26
|
+
'genproclimit' => -1,
|
27
|
+
'difficulty' => 1805700.83619367,
|
28
|
+
'hashespersec' => 0,
|
29
|
+
'testnet' => false,
|
30
|
+
'keypoololdest' => 1313766189,
|
31
|
+
'paytxfee' => 0.0,
|
32
|
+
'errors' => ""
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
service 'getblockcount' do
|
38
|
+
it "should produce the expected result" do
|
39
|
+
result.should == 141972
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
service 'getblocknumber' do
|
44
|
+
it "should produce the expected result" do
|
45
|
+
result.should == 141972
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
service 'getconnectioncount' do
|
50
|
+
it "should produce the expected result" do
|
51
|
+
result.should == 8
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
service 'getdifficulty' do
|
56
|
+
it "should produce the expected result" do
|
57
|
+
result.should == 1805700.83619367
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
service 'getgenerate' do
|
62
|
+
it "should produce the expected result" do
|
63
|
+
result.should be_false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
service 'gethashespersec' do
|
68
|
+
it "should produce the expected result" do
|
69
|
+
result.should == 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
service 'listreceivedbyaddress' do
|
74
|
+
context 'without params' do
|
75
|
+
it "should produce the expected result" do
|
76
|
+
result.should == [{
|
77
|
+
'address' => "1234",
|
78
|
+
'account' => "",
|
79
|
+
'label' => "",
|
80
|
+
'amount' => 0.001,
|
81
|
+
'confirmations' => 180}]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with minconf 0' do
|
86
|
+
it "should produce the expected result" do
|
87
|
+
result(0).should == [{
|
88
|
+
'address' => "1234",
|
89
|
+
'account' => "",
|
90
|
+
'label' => "",
|
91
|
+
'amount' => 0.001,
|
92
|
+
'confirmations' => 180}]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with minconf 0 and includeempty true' do
|
97
|
+
it "should produce the expected result" do
|
98
|
+
result(0, true).should == [{
|
99
|
+
'address' => "1234",
|
100
|
+
'account' => "",
|
101
|
+
'label' => "",
|
102
|
+
'amount' => 0.001,
|
103
|
+
'confirmations' => 180}]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bitcoin::Request do
|
4
|
+
it "should omit null arguments and everything after them" do
|
5
|
+
# bitcoin rejects null values even for optional params. Since
|
6
|
+
# even params following those may have default non-nil values,
|
7
|
+
# we'll assume the first non-nil value marks a set of optional
|
8
|
+
# params, and drop it and everything following it.
|
9
|
+
|
10
|
+
req = Bitcoin::Request.new('svc', [1, nil, nil, nil])
|
11
|
+
req.params.should == [1]
|
12
|
+
|
13
|
+
req = Bitcoin::Request.new('svc', [nil])
|
14
|
+
req.params.should == []
|
15
|
+
|
16
|
+
req = Bitcoin::Request.new('svc', [1, nil, nil, 1, nil])
|
17
|
+
req.params.should == [1]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bitcoin do
|
4
|
+
before :each do
|
5
|
+
FakeWeb.register_uri(:post, "http://user:pass@localhost:8332", :response => fixture('getbalance'))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "as a function" do
|
9
|
+
cli = Bitcoin($user, $pass)
|
10
|
+
cli.balance.should == 0.001
|
11
|
+
end
|
12
|
+
|
13
|
+
it "DSL, included" do
|
14
|
+
class << self
|
15
|
+
include Bitcoin
|
16
|
+
end
|
17
|
+
|
18
|
+
username $user
|
19
|
+
password $pass
|
20
|
+
|
21
|
+
balance.should == 0.001
|
22
|
+
end
|
23
|
+
|
24
|
+
it "DSL, extended" do
|
25
|
+
class << self
|
26
|
+
include Bitcoin
|
27
|
+
|
28
|
+
username $user
|
29
|
+
password $pass
|
30
|
+
|
31
|
+
balance.should == 0.001
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|