-
Notifications
You must be signed in to change notification settings - Fork 18
/
bitcoin.rb
128 lines (105 loc) · 3.61 KB
/
bitcoin.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#
# cellminer - Bitcoin miner for the Cell Broadband Engine Architecture
# Copyright © 2011 Robert Leslie
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License (version 2) as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
require 'uri'
require 'json'
require 'net/http/persistent'
module Bitcoin
CONFFILE = File.join(ENV['HOME'], '.bitcoin', 'bitcoin.conf')
def self.conf
parms = File.read(CONFFILE).split("\n")
parms.inject(Hash.new) {|h, p| h.merge! Hash[*p.split("=")] }
end
def self.rpc_proxy(server = {}, user_agent = nil)
conf = self.conf rescue Hash.new
username = server.delete(:username) || conf['rpcuser']
password = server.delete(:password) || conf['rpcpassword']
default = {
:host => 'localhost',
:port => 8332,
:path => '/'
}
uri = URI::HTTP.build(default.merge(server))
# This is a workaround; URI needs escaped username and password
if username
uri.user = URI.escape username, /[^#{URI::PATTERN::UNRESERVED}]/
uri.password = URI.escape password, /[^#{URI::PATTERN::UNRESERVED}]/
end
RPCProxy.new(uri, user_agent)
end
class RPCProxy
LONG_POLL_TIMEOUT = 60 * 60 * 12
class JSONRPCError < StandardError
attr_reader :code
def initialize(json)
super json['message']
@code = json['code']
end
end
def initialize(uri, user_agent = nil, timeout = nil)
case uri
when URI
when String
uri = URI.parse(uri)
else
raise ArgumentError, "bad URI given"
end
@uri = uri
@user_agent = user_agent
@session = Net::HTTP::Persistent.new
@session.read_timeout = timeout if timeout
def @session.idempotent? req
# All of the RPC methods we use are idempotent
true
end
end
def method_missing(method, *params)
request = Net::HTTP::Post.new(@uri.path)
request['User-Agent'] = @user_agent if @user_agent
# This is a workaround; URI needs escaped username and password
# strings but Net:HTTP requires them unescaped (credentials get
# base64-encoded anyway)
request.basic_auth(URI.unescape(@uri.user),
URI.unescape(@uri.password)) if @uri.user
request.content_type = 'application/json'
request.body = {
id: 'jsonrpc',
method: method,
params: params
}.to_json
begin
response = @session.request(@uri, request)
response.value # raises error on all non-success responses
rescue Errno::EINTR
retry
end
json = JSON.parse(response.body)
raise JSONRPCError.new(json['error']) if json['error']
if block_given? and poll_path = response['X-Long-Polling']
poll_uri = URI.parse(poll_path)
poll_uri = @uri.merge(poll_uri) if poll_uri.kind_of?(URI::Generic)
poll_uri.path = "/" if poll_uri.path.empty?
if @uri.user
poll_uri.user = @uri.user
poll_uri.password = @uri.password
end
yield self.class.new(poll_uri, @user_agent, LONG_POLL_TIMEOUT)
end
json['result']
end
end
end