bitcoin-client 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/.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
|