bitcoinrb 0.1.3 → 0.1.4

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.
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