bitcoinrb 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/README.md +31 -9
  4. data/bitcoinrb.conf.sample +0 -0
  5. data/bitcoinrb.gemspec +6 -2
  6. data/exe/bitcoinrb-cli +2 -2
  7. data/lib/bitcoin/block_header.rb +9 -2
  8. data/lib/bitcoin/chainparams/regtest.yml +1 -1
  9. data/lib/bitcoin/constants.rb +3 -0
  10. data/lib/bitcoin/key.rb +70 -5
  11. data/lib/bitcoin/logger.rb +25 -1
  12. data/lib/bitcoin/message/addr.rb +0 -39
  13. data/lib/bitcoin/message/headers.rb +1 -0
  14. data/lib/bitcoin/message/inventory.rb +4 -0
  15. data/lib/bitcoin/message/network_addr.rb +44 -0
  16. data/lib/bitcoin/message/version.rb +8 -3
  17. data/lib/bitcoin/message.rb +12 -0
  18. data/lib/bitcoin/mnemonic.rb +2 -2
  19. data/lib/bitcoin/network/connection.rb +14 -1
  20. data/lib/bitcoin/network/message_handler.rb +52 -19
  21. data/lib/bitcoin/network/peer.rb +142 -5
  22. data/lib/bitcoin/network/peer_discovery.rb +16 -8
  23. data/lib/bitcoin/network/pool.rb +39 -14
  24. data/lib/bitcoin/network.rb +0 -1
  25. data/lib/bitcoin/node/cli.rb +66 -0
  26. data/lib/bitcoin/node/configuration.rb +37 -0
  27. data/lib/bitcoin/node/spv.rb +13 -3
  28. data/lib/bitcoin/node.rb +2 -1
  29. data/lib/bitcoin/rpc/http_server.rb +56 -0
  30. data/lib/bitcoin/rpc/request_handler.rb +84 -0
  31. data/lib/bitcoin/rpc.rb +6 -0
  32. data/lib/bitcoin/script/multisig.rb +92 -0
  33. data/lib/bitcoin/script/script.rb +17 -7
  34. data/lib/bitcoin/script/script_interpreter.rb +2 -38
  35. data/lib/bitcoin/store/chain_entry.rb +64 -0
  36. data/lib/bitcoin/store/db/level_db.rb +101 -0
  37. data/lib/bitcoin/store/db.rb +9 -0
  38. data/lib/bitcoin/store/spv_chain.rb +96 -0
  39. data/lib/bitcoin/store.rb +5 -1
  40. data/lib/bitcoin/tx.rb +6 -2
  41. data/lib/bitcoin/version.rb +1 -1
  42. data/lib/bitcoin/wallet/account.rb +82 -0
  43. data/lib/bitcoin/wallet/base.rb +84 -0
  44. data/lib/bitcoin/wallet/db.rb +57 -0
  45. data/lib/bitcoin/wallet/master_key.rb +100 -0
  46. data/lib/bitcoin/wallet.rb +8 -0
  47. data/lib/bitcoin.rb +4 -0
  48. data/lib/openassets/payload.rb +6 -2
  49. metadata +70 -13
  50. data/lib/bitcoin/node/spv_block_chain.rb +0 -15
  51. data/lib/bitcoin/store/spv_block_store.rb +0 -11
@@ -6,7 +6,8 @@ module Bitcoin
6
6
 
7
7
  # handle p2p message.
8
8
  def handle(message)
9
- logger.info "handle message #{message.bth}"
9
+ peer.last_recv = Time.now.to_i
10
+ peer.bytes_recv += message.bytesize
10
11
  begin
11
12
  parse(message)
12
13
  rescue Bitcoin::Message::Error => e
@@ -20,7 +21,7 @@ module Bitcoin
20
21
  command, payload, rest = parse_header
21
22
  return unless command
22
23
 
23
- handle_command(command, payload)
24
+ defer_handle_command(command, payload)
24
25
  @message = ""
25
26
  parse(rest) if rest && rest.bytesize > 0
26
27
  end
@@ -40,8 +41,20 @@ module Bitcoin
40
41
  [command, payload, rest]
41
42
  end
42
43
 
44
+ # handle command with EM#defer
45
+ def defer_handle_command(command, payload)
46
+ operation = proc {handle_command(command, payload)}
47
+ callback = proc{|result|}
48
+ errback = proc{|e|
49
+ logger.error("error occurred. #{e.message}")
50
+ logger.error(e.backtrace)
51
+ peer.handle_error(e)
52
+ }
53
+ EM.defer(operation, callback, errback)
54
+ end
55
+
43
56
  def handle_command(command, payload)
44
- logger.info("[#{addr}] process command #{command}. payload = #{payload.bth}")
57
+ logger.info("[#{addr}] process command #{command}.")
45
58
  case command
46
59
  when Bitcoin::Message::Version::COMMAND
47
60
  on_version(Bitcoin::Message::Version.parse_from_payload(payload))
@@ -77,6 +90,8 @@ module Bitcoin
77
90
  on_send_cmpct(Bitcoin::Message::SendCmpct.parse_from_payload(payload))
78
91
  when Bitcoin::Message::Inv::COMMAND
79
92
  on_inv(Bitcoin::Message::Inv.parse_from_payload(payload))
93
+ when Bitcoin::Message::MerkleBlock::COMMAND
94
+ on_merkle_block(Bitcoin::Message::MerkleBlock.parse_from_payload(payload))
80
95
  else
81
96
  logger.warn("unsupported command received. #{command}")
82
97
  close("with command #{command}")
@@ -85,14 +100,10 @@ module Bitcoin
85
100
 
86
101
  def send_message(msg)
87
102
  logger.info "send message #{msg.class::COMMAND}"
88
- send_data(msg.to_pkt)
89
- end
90
-
91
- # start handshake
92
- def begin_handshake
93
- logger.info "begin handshake with #{addr}"
94
- ver = Bitcoin::Message::Version.new(remote_addr: addr, start_height: 0) # TODO use start_height in wallet
95
- send_message(ver)
103
+ pkt = msg.to_pkt
104
+ peer.last_send = Time.now.to_i
105
+ peer.bytes_sent = pkt.bytesize
106
+ send_data(pkt)
96
107
  end
97
108
 
98
109
  def handshake_done
@@ -114,6 +125,7 @@ module Bitcoin
114
125
 
115
126
  def on_get_addr
116
127
  logger.info('receive getaddr message.')
128
+ peer.send_addrs
117
129
  end
118
130
 
119
131
  def on_addr(addr)
@@ -138,26 +150,31 @@ module Bitcoin
138
150
 
139
151
  def on_pong(pong)
140
152
  logger.info("receive pong message. #{pong.build_json}")
141
- # TODO calculate response
153
+ if pong.nonce == peer.last_ping_nonce
154
+ peer.last_ping_nonce = nil
155
+ peer.last_pong = Time.now.to_i
156
+ else
157
+ logger.debug "The remote peer sent the wrong nonce (#{pong.nonce})."
158
+ end
142
159
  end
143
160
 
144
161
  def on_get_headers(headers)
145
- logger.info("receive getheaders message. #{headers.build_json}")
162
+ logger.info('receive getheaders message.')
146
163
  # TODO
147
164
  end
148
165
 
149
166
  def on_headers(headers)
150
- logger.info("receive headers message. #{headers.build_json}")
151
- # TODO
167
+ logger.info('receive headers message.')
168
+ peer.handle_headers(headers)
152
169
  end
153
170
 
154
171
  def on_block(block)
155
- logger.info("receive block message. #{block.build_json}")
172
+ logger.info('receive block message.')
156
173
  # TODO
157
174
  end
158
175
 
159
176
  def on_tx(tx)
160
- logger.info("receive tx message. #{tx.build_json}")
177
+ logger.info('receive tx message.')
161
178
  # TODO
162
179
  end
163
180
 
@@ -182,10 +199,26 @@ module Bitcoin
182
199
  end
183
200
 
184
201
  def on_inv(inv)
185
- logger.info("receive inv message. #{inv.build_json}")
186
- # TODO
202
+ logger.info('receive inv message.')
203
+ blocks = []
204
+ txs = []
205
+ inv.inventories.each do |i|
206
+ case i.identifier
207
+ when Bitcoin::Message::Inventory::MSG_TX
208
+ txs << i.hash
209
+ when Bitcoin::Message::Inventory::MSG_BLOCK
210
+ blocks << i.hash
211
+ else
212
+ logger.warn("[#{addr}] peer sent unknown inv type: #{i.identifier}")
213
+ end
214
+ end
215
+ logger.info("receive block= #{blocks.size}, txs: #{txs.size}")
216
+ peer.handle_block_inv(blocks) unless blocks.empty?
187
217
  end
188
218
 
219
+ def on_merkle_block(merkle_block)
220
+ logger.info("receive merkle block message. #{merkle_block.build_json}")
221
+ end
189
222
 
190
223
  end
191
224
  end
@@ -4,21 +4,52 @@ module Bitcoin
4
4
  # remote peer class.
5
5
  class Peer
6
6
 
7
+ # Interval for pinging peers.
8
+ PING_INTERVAL = 30
9
+
10
+ attr_reader :logger
11
+ attr_accessor :id
12
+ attr_accessor :local_version
13
+ attr_accessor :last_send
14
+ attr_accessor :last_recv
15
+ attr_accessor :bytes_sent
16
+ attr_accessor :bytes_recv
17
+ attr_accessor :conn_time
18
+ attr_accessor :last_ping
19
+ attr_accessor :last_ping_nonce
20
+ attr_accessor :last_pong
21
+ attr_accessor :min_ping
22
+ attr_accessor :outbound # TODO need implements to accept inbound connection
23
+ attr_accessor :best_hash
24
+ attr_accessor :best_height
7
25
  # remote peer info
8
26
  attr_reader :host
9
27
  attr_reader :port
10
28
  # remote peer connection
11
29
  attr_accessor :conn
12
30
  attr_accessor :connected
31
+ attr_accessor :primary
13
32
  # parent pool
14
33
  attr_reader :pool
34
+ attr_reader :chain
15
35
  attr_accessor :fee_rate
16
36
 
17
37
  def initialize(host, port, pool)
18
38
  @host = host
19
39
  @port = port
20
40
  @pool = pool
41
+ @chain = pool.chain
21
42
  @connected = false
43
+ @primary = false
44
+ @logger = Bitcoin::Logger.create(:debug)
45
+ @outbound = true
46
+ @best_hash = -1
47
+ @best_height = -1
48
+ @min_ping = -1
49
+ @bytes_sent = 0
50
+ @bytes_recv = 0
51
+ current_height = @chain.latest_block.height
52
+ @local_version = Bitcoin::Message::Version.new(remote_addr: addr, start_height: current_height)
22
53
  end
23
54
 
24
55
  def connect
@@ -29,24 +60,130 @@ module Bitcoin
29
60
  @connected
30
61
  end
31
62
 
63
+ def outbound?
64
+ @outbound
65
+ end
66
+
32
67
  def addr
33
68
  "#{host}:#{port}"
34
69
  end
35
70
 
71
+ # calculate ping-pong time.
72
+ def ping_time
73
+ last_pong ? (last_pong - last_ping) / 1e6 : -1
74
+ end
75
+
76
+ # set last pong
77
+ def last_pong=(time)
78
+ @last_pong = time
79
+ @min_ping = ping_time if min_ping == -1 || ping_time < min_ping
80
+ end
81
+
36
82
  def post_handshake
37
83
  @connected = true
38
84
  pool.handle_new_peer(self)
85
+ # require remote peer to use headers message instead fo inv message.
86
+ conn.send_message(Bitcoin::Message::SendHeaders.new)
87
+ EM.add_periodic_timer(PING_INTERVAL) {send_ping}
88
+ end
89
+
90
+ # start block header download
91
+ def start_block_header_download
92
+ logger.info("[#{addr}] start block header download.")
93
+ get_headers = Bitcoin::Message::GetHeaders.new(
94
+ Bitcoin.chain_params.protocol_version, [chain.latest_block.hash])
95
+ conn.send_message(get_headers)
39
96
  end
40
97
 
41
98
  # broadcast tx.
42
99
  def broadcast_tx(tx)
43
- send_message(Bitcoin::Message::Tx.new(tx, ))
100
+ conn.send_message(Bitcoin::Message::Tx.new(tx, support_witness?))
101
+ end
102
+
103
+ # check the remote peer support witness.
104
+ def support_witness?
105
+ return false unless remote_version
106
+ remote_version.services & Bitcoin::Message::SERVICE_FLAGS[:witness] > 0
107
+ end
108
+
109
+ # check the remote peer supports compact block.
110
+ def support_cmpct?
111
+ return false if remote_version.version < Bitcoin::Message::VERSION[:compact]
112
+ return true unless local_version.services & Bitcoin::Message::SERVICE_FLAGS[:witness] > 0
113
+ return false unless support_witness?
114
+ remote_version.version >= Bitcoin::Message::VERSION[:compact_witness]
115
+ end
116
+
117
+ # get peer's block type.
118
+ def block_type
119
+ Bitcoin::Message::Inventory::MSG_FILTERED_BLOCK # TODO need other implementation
120
+ end
121
+
122
+ # get remote peer's version message.
123
+ # @return [Bitcoin::Message::Version]
124
+ def remote_version
125
+ conn.version
126
+ end
127
+
128
+ # Whether to try and download blocks and transactions from this peer.
129
+ def primary?
130
+ primary
131
+ end
132
+
133
+ # handle headers message
134
+ # @params [Bitcoin::Message::Headers]
135
+ def handle_headers(headers)
136
+ headers.headers.each do |header|
137
+ break unless header.valid?
138
+ entry = chain.append_header(header)
139
+ @best_hash = entry.hash
140
+ @best_height = entry.height
141
+ end
142
+ start_block_header_download if headers.headers.size > 0 # next header download
143
+ end
144
+
145
+ # handle error
146
+ def handle_error(e)
147
+ pool.handle_error(e)
148
+ end
149
+
150
+ # close peer connection.
151
+ def close(msg = '')
152
+ conn.close(msg)
153
+ end
154
+
155
+ # generate Bitcoin::Message::NetworkAddr object from this peer info.
156
+ # @return [Bitcoin::Message::NetworkAddr]
157
+ def to_network_addr
158
+ v = remote_version
159
+ addr = Bitcoin::Message::NetworkAddr.new
160
+ addr.time = v.timestamp
161
+ addr.services = v.services
162
+ addr.ip = host
163
+ addr.port = port
164
+ addr
165
+ end
166
+
167
+ # send +addr+ message to remote peer
168
+ def send_addrs
169
+ addrs = pool.peers.select{|p|p != self}.map(&:to_network_addr)
170
+ conn.send_message(Bitcoin::Message::Addr.new(addrs))
171
+ end
172
+
173
+ # handle block inv message.
174
+ def handle_block_inv(hashes)
175
+ getdata = Bitcoin::Message::GetData.new(
176
+ hashes.map{|h|Bitcoin::Message::Inventory.new(block_type, h)})
177
+ conn.send_message(getdata)
44
178
  end
45
179
 
46
- # check the remote peer support segwit.
47
- def support_segwit?
48
- return false unless conn.version
49
- conn.version.services & Bitcoin::Message::SERVICE_FLAGS[:witness] > 0
180
+ # send ping message.
181
+ def send_ping
182
+ ping = Bitcoin::Message::Ping.new
183
+ @last_ping = Time.now.to_i
184
+ @last_pong = -1
185
+ @last_ping_nonce = ping.nonce
186
+ conn.send_message(ping)
50
187
  end
51
188
 
52
189
  end
@@ -3,32 +3,40 @@ module Bitcoin
3
3
 
4
4
  class PeerDiscovery
5
5
 
6
- attr_reader :logger
6
+ attr_reader :logger, :configuration
7
7
 
8
- def initialize
8
+ def initialize(configuration)
9
9
  @logger = Bitcoin::Logger.create(:debug)
10
+ @configuration = configuration
10
11
  end
11
12
 
12
13
  # get peer addresses, from DNS seeds.
13
14
  def peers
14
15
  # TODO add find from previous connected peer at first.
15
- find_from_dns_seeds
16
+ (find_from_dns_seeds + seeds).uniq
16
17
  end
17
18
 
18
19
  private
19
20
 
21
+ def dns_seeds
22
+ Bitcoin.chain_params.dns_seeds || []
23
+ end
24
+
25
+ def seeds
26
+ [*configuration.conf[:connect]]
27
+ end
28
+
20
29
  def find_from_dns_seeds
21
30
  logger.debug 'discover peer address from DNS seeds.'
22
- Bitcoin.chain_params.dns_seeds.map {|seed|
31
+ dns_seeds.map { |seed|
23
32
  begin
24
33
  Socket.getaddrinfo(seed, Bitcoin.chain_params.default_port).map{|a|a[2]}.uniq
25
34
  rescue SocketError => e
26
35
  logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
36
+ nil
27
37
  end
28
- }.flatten
38
+ }.flatten.compact
29
39
  end
30
-
31
40
  end
32
-
33
41
  end
34
- end
42
+ end
@@ -20,13 +20,13 @@ module Bitcoin
20
20
  attr_reader :peer_discovery
21
21
  attr_accessor :started
22
22
 
23
- def initialize
23
+ def initialize(chain, configuration)
24
24
  @peers = []
25
25
  @pending_peers = []
26
26
  @max_outbound = MAX_OUTBOUND_CONNECTIONS
27
- @chain = Bitcoin::Node::SPVBlockChain.new
27
+ @chain = chain
28
28
  @logger = Bitcoin::Logger.create(:debug)
29
- @peer_discovery = PeerDiscovery.new
29
+ @peer_discovery = PeerDiscovery.new(configuration)
30
30
  @started = false
31
31
  end
32
32
 
@@ -35,14 +35,14 @@ module Bitcoin
35
35
  raise 'Cannot start a peer pool twice.' if started
36
36
  logger.debug 'Start connecting other pears.'
37
37
  addr_list = peer_discovery.peers
38
+
38
39
  port = Bitcoin.chain_params.default_port
39
- Parallel.map(addr_list, in_processes: 3) do |ip|
40
- if peers.size < MAX_OUTBOUND_CONNECTIONS
41
- EM.run do
42
- peer = Peer.new(ip, port, self)
43
- pending_peers << peer
44
- peer.connect
45
- end
40
+ EM::Iterator.new(addr_list, Bitcoin::PARALLEL_THREAD).each do |ip, iter|
41
+ if pending_peers.size < MAX_OUTBOUND_CONNECTIONS
42
+ peer = Peer.new(ip, port, self)
43
+ pending_peers << peer
44
+ peer.connect
45
+ iter.next
46
46
  end
47
47
  end
48
48
  @started = true
@@ -50,25 +50,50 @@ module Bitcoin
50
50
 
51
51
  # detect new peer connection.
52
52
  def handle_new_peer(peer)
53
- logger.debug "connected new peer #{peer.addr}"
53
+ logger.debug "connected new peer #{peer.addr}."
54
+ peer.id = allocate_peer_id
55
+ unless peers.find(&:primary?)
56
+ peer.primary = true
57
+ peer.start_block_header_download
58
+ end
54
59
  peers << peer
55
60
  pending_peers.delete(peer)
56
61
  end
57
62
 
58
63
  # terminate peers.
59
64
  def terminate
60
- peers.each {|peer| peer.close('terminate')}
65
+ peers.each { |peer| peer.close('terminate') }
66
+ pending_peers.each { |peer| peer.close('terminate') }
61
67
  @peers = []
62
68
  @started = false
63
69
  end
64
70
 
71
+ # broadcast tx to connecting peer.
65
72
  def broadcast(tx)
66
- peers.each {|peer| peer.broadcast(tx) }
73
+ peers.each { |peer| peer.broadcast_tx(tx) }
74
+ end
75
+
76
+ def handle_error(e)
77
+ terminate
67
78
  end
68
79
 
69
80
  private
70
81
 
82
+ # get primary peer
83
+ def primary_peer
84
+ peers.find(&:primary?)
85
+ end
86
+
87
+ # allocate new peer id
88
+ def allocate_peer_id
89
+ id = 0
90
+ until peers.empty? || peers.find{|p|p.id == id}.nil?
91
+ id += 1
92
+ end
93
+ id
94
+ end
95
+
71
96
  end
72
97
 
73
98
  end
74
- end
99
+ end
@@ -1,4 +1,3 @@
1
- require 'parallel'
2
1
  require 'eventmachine'
3
2
 
4
3
  module Bitcoin
@@ -0,0 +1,66 @@
1
+ require 'rest-client'
2
+ require 'thor'
3
+ require 'json'
4
+
5
+ module Bitcoin
6
+ module Node
7
+
8
+ class CLI < Thor
9
+
10
+ class_option :network, aliases: '-n', default: :mainnet
11
+
12
+ desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
13
+ def getblockchaininfo
14
+ request('getblockchaininfo')
15
+ end
16
+
17
+ desc 'stop', 'Stop Bitcoin server.'
18
+ def stop
19
+ request('stop')
20
+ end
21
+
22
+ desc 'getblockheader "hash" ( verbose )', 'If verbose is false, returns a string that is serialized, hex-encoded data for blockheader "hash". If verbose is true, returns an Object with information about blockheader <hash>.'
23
+ def getblockheader(hash, verbose = true)
24
+ verbose = verbose.is_a?(String) ? (verbose == 'true') : verbose
25
+ request('getblockheader', hash, verbose)
26
+ end
27
+
28
+ desc 'getpeerinfo', 'Returns data about each connected network node as a json array of objects.'
29
+ def getpeerinfo
30
+ request('getpeerinfo')
31
+ end
32
+
33
+ desc 'sendrawtransaction', 'Submits raw transaction (serialized, hex-encoded) to local node and network.'
34
+ def sendrawtransaction(hex_tx)
35
+ request('sendrawtransaction', hex_tx)
36
+ end
37
+
38
+ private
39
+
40
+ def config
41
+ opts = {}
42
+ opts[:network] = options['network'] if options['network']
43
+ @conf ||= Bitcoin::Node::Configuration.new(opts)
44
+ end
45
+
46
+ def request(command, *params)
47
+ data = {
48
+ :method => command,
49
+ :params => params,
50
+ :id => 'jsonrpc'
51
+ }
52
+ begin
53
+ RestClient::Request.execute(method: :post, url: config.server_url, payload: data.to_json,
54
+ headers: {content_type: :json}) do |response, request, result|
55
+ return false if !result.kind_of?(Net::HTTPSuccess) && response.empty?
56
+ json = JSON.parse(response.to_str)
57
+ puts JSON.pretty_generate(json)
58
+ end
59
+ rescue Exception => e
60
+ puts e.message
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ require 'iniparse'
2
+
3
+ module Bitcoin
4
+ module Node
5
+ class Configuration
6
+
7
+ attr_reader :conf
8
+
9
+ def initialize(opts = {})
10
+ # TODO apply configuration file.
11
+ opts[:network] = :mainnet unless opts[:network]
12
+ Bitcoin.chain_params = opts[:network]
13
+
14
+ begin
15
+ ini_file = IniParse.parse(File.read("#{Bitcoin.base_dir}/bitcoinrb.conf"))
16
+ @conf = Hash[ ini_file.to_h['__anonymous__'].map{|k,v| [k.to_sym, v] } ]
17
+ rescue => e
18
+ @conf = {}
19
+ end
20
+ @conf.merge!(opts)
21
+ end
22
+
23
+ def host
24
+ 'localhost'
25
+ end
26
+
27
+ def port
28
+ Bitcoin.chain_params.default_port - 1
29
+ end
30
+
31
+ def server_url
32
+ "http://#{host}:#{port}"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -4,21 +4,31 @@ module Bitcoin
4
4
  # SPV class
5
5
  class SPV
6
6
 
7
+ attr_reader :chain
7
8
  attr_reader :pool
8
9
  attr_reader :logger
9
10
  attr_accessor :running
11
+ attr_reader :configuration
12
+ attr_accessor :server
10
13
 
11
- def initialize
12
- @pool = Bitcoin::Network::Pool.new
14
+ def initialize(configuration)
15
+ @chain = Bitcoin::Store::SPVChain.new
16
+ @configuration = configuration
17
+ @pool = Bitcoin::Network::Pool.new(@chain, @configuration)
13
18
  @logger = Bitcoin::Logger.create(:debug)
14
19
  @running = false
15
20
  end
16
21
 
17
22
  # open the node.
18
23
  def run
24
+ # TODO need process running check.
19
25
  return if running
20
26
  logger.debug 'SPV node start running.'
21
- pool.start
27
+ EM.run do
28
+ # EM.start_server('0.0.0.0', Bitcoin.chain_params.default_port, Bitcoin::Network::InboundConnector, self)
29
+ pool.start
30
+ @server = Bitcoin::RPC::HttpServer.run(self, configuration.port)
31
+ end
22
32
  end
23
33
 
24
34
  # close the node.
data/lib/bitcoin/node.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Bitcoin
2
2
  module Node
3
3
  autoload :SPV, 'bitcoin/node/spv'
4
- autoload :SPVBlockChain, 'bitcoin/node/spv_block_chain'
4
+ autoload :CLI, 'bitcoin/node/cli'
5
+ autoload :Configuration, 'bitcoin/node/configuration'
5
6
  end
6
7
  end
@@ -0,0 +1,56 @@
1
+ require 'evma_httpserver'
2
+ require 'json'
3
+
4
+ module Bitcoin
5
+ module RPC
6
+
7
+ # Bitcoinrb RPC server.
8
+ class HttpServer < EM::Connection
9
+ include EM::HttpServer
10
+ include RequestHandler
11
+
12
+ attr_reader :node
13
+ attr_accessor :logger
14
+
15
+ def initialize(node)
16
+ @node = node
17
+ @logger = Bitcoin::Logger.create(:debug)
18
+ end
19
+
20
+ def post_init
21
+ super
22
+ logger.debug 'start http server.'
23
+ end
24
+
25
+ def self.run(node, port = 8332)
26
+ EM.start_server('0.0.0.0', port, HttpServer, node)
27
+ end
28
+
29
+ # process http request.
30
+ def process_http_request
31
+ operation = proc {
32
+ command, args = parse_json_params
33
+ logger.debug("process http request. command = #{command}")
34
+ send(command, *args).to_json
35
+ }
36
+ callback = proc{ |result|
37
+ response = EM::DelegatedHttpResponse.new(self)
38
+ response.status = 200
39
+ response.content_type 'application/json'
40
+ response.content = result
41
+ response.send_response
42
+ }
43
+ EM.defer(operation, callback)
44
+ end
45
+
46
+ # parse request parameter.
47
+ # @return [Array] the array of command and args
48
+ def parse_json_params
49
+ params = JSON.parse(@http_post_content)
50
+ [params['method'], params['params']]
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end