Class: Bitcoin::Network::CommandHandler

Inherits:
EM::Connection
  • Object
show all
Defined in:
lib/bitcoin/network/command_handler.rb

Overview

Started by the Node, accepts connections from CommandClient and answers requests or registers for events and notifies the clients when they happen.

Instance Method Summary collapse

Constructor Details

#initialize(node) ⇒ CommandHandler

create new CommandHandler



9
10
11
12
13
14
# File 'lib/bitcoin/network/command_handler.rb', line 9

def initialize node
  @node = node
  @node.command_connections << self
  @buf = BufferedTokenizer.new("\x00")
  @lock = Monitor.new
end

Instance Method Details

#format_uptime(t) ⇒ Object

format node uptime



166
167
168
169
170
171
# File 'lib/bitcoin/network/command_handler.rb', line 166

def format_uptime t
  mm, ss = t.divmod(60)            #=> [4515, 21]
  hh, mm = mm.divmod(60)           #=> [75, 15]
  dd, hh = hh.divmod(24)           #=> [3, 3]
  "%02d:%02d:%02d:%02d" % [dd, hh, mm, ss]
end

#handle_addrs(count = 32) ⇒ Object

display known peer addrs (used by bin/bitcoin_dns_seed) bitcoin_node addrs [count]



135
136
137
138
139
140
141
# File 'lib/bitcoin/network/command_handler.rb', line 135

def handle_addrs count = 32
  @node.addrs.weighted_sample(count.to_i) do |addr|
    Time.now.tv_sec + 7200 - addr.time
  end.map do |addr|
    [addr.ip, addr.port, Time.now.tv_sec - addr.time] rescue nil
  end.compact
end

#handle_configObject

display configuration hash currently used bitcoin_node config



86
87
88
# File 'lib/bitcoin/network/command_handler.rb', line 86

def handle_config
  @node.config
end

#handle_connect(*args) ⇒ Object

connect to given peer(s) bitcoin_node connect :[,:]



103
104
105
106
# File 'lib/bitcoin/network/command_handler.rb', line 103

def handle_connect *args
  args.each {|a| @node.connect_peer(*a.split(':')) }
  {:state => "Connecting..."}
end

#handle_connectionsObject

display connected peers bitcoin_node connections



92
93
94
95
96
97
98
99
# File 'lib/bitcoin/network/command_handler.rb', line 92

def handle_connections
  @node.connections.sort{|x,y| y.uptime <=> x.uptime}.map{|c|
    "#{c.host.rjust(15)}:#{c.port} [state: #{c.state}, " +
    "version: #{c.version.version rescue '?'}, " +
    "block: #{c.version.block rescue '?'}, " +
    "uptime: #{format_uptime(c.uptime) rescue 0}, " +
    "client: #{c.version.user_agent rescue '?'}]" }
end

#handle_disconnect(*args) ⇒ Object

disconnect peer(s) bitcoin_node disconnect :[,,]



110
111
112
113
114
115
116
117
# File 'lib/bitcoin/network/command_handler.rb', line 110

def handle_disconnect *args
  args.each do |c|
    host, port = *c.split(":")
    conn = @node.connections.select{|c| c.host == host && c.port == port.to_i}.first
    conn.close_connection  if conn
  end
  {:state => "Disconnected"}
end

#handle_getaddrObject

trigger node to ask for new peer addrs bitcoin_node getaddr



128
129
130
131
# File 'lib/bitcoin/network/command_handler.rb', line 128

def handle_getaddr
  @node.connections.sample.send_getaddr
  {:state => "Sending getaddr..."}
end

#handle_getblocksObject

trigger node to ask peers for new blocks bitcoin_node getblocks



121
122
123
124
# File 'lib/bitcoin/network/command_handler.rb', line 121

def handle_getblocks
  @node.connections.sample.send_getblocks
  {:state => "Sending getblocks..."}
end

#handle_helpObject

list all commands bitcoin_node help



161
162
163
# File 'lib/bitcoin/network/command_handler.rb', line 161

def handle_help
  self.methods.grep(/^handle_(.*?)/).map {|m| m.to_s.sub(/^(.*?)_/, '')}
end

#handle_infoObject

display various statistics bitcoin_node info



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/bitcoin/network/command_handler.rb', line 68

def handle_info
  blocks = @node.connections.map(&:version).compact.map(&:block) rescue nil
  {
    :blocks => "#{@node.store.get_depth} (#{(blocks.inject{|a,b| a+=b;a} / blocks.size rescue '?')})#{@node.in_sync ? ' sync' : ''}",
    :addrs => "#{@node.addrs.select{|a| a.alive?}.size} (#{@node.addrs.size})",
    :connections => "#{@node.connections.select{|c| c.state == :connected}.size} (#{@node.connections.size})",
    :queue => @node.queue.size,
    :inv_queue => @node.inv_queue.size,
    :inv_cache => @node.inv_cache.size,
    :network => @node.config[:network],
    :storage => @node.config[:storage],
    :version => Bitcoin::Protocol::VERSION,
    :uptime => format_uptime(@node.uptime),
  }
end

#handle_monitor(*channels) ⇒ Object

handle monitor command; subscribe client to specified channels (+block+, tx, connection) bitcoin_node monitor block bitcoin_node monitor "block tx connection"



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/bitcoin/network/command_handler.rb', line 48

def handle_monitor *channels
  channels.each do |channel|
    @node.notifiers[channel.to_sym].subscribe do |*data|
      respond("monitor", [channel, *data])
    end
    case channel.to_sym
    when :block
      head = Bitcoin::P::Block.new(@node.store.get_head.to_payload) rescue nil
      respond("monitor", ["block", [head, @node.store.get_depth.to_s]])  if head
    when :connection
      @node.connections.select {|c| c.connected?}.each do |conn|
        respond("monitor", [:connection, [:connected, conn.info]])
      end
    end
  end
  nil
end

#handle_relay_tx(data) ⇒ Object

relay given transaction (in hex) bitcoin_node relay_tx



145
146
147
148
149
150
# File 'lib/bitcoin/network/command_handler.rb', line 145

def handle_relay_tx data
  tx = Bitcoin::Protocol::Tx.from_hash(data)
  @node.relay_tx(tx)
rescue
  {:error => $!}
end

#handle_stopObject

stop bitcoin node bitcoin_node stop



154
155
156
157
# File 'lib/bitcoin/network/command_handler.rb', line 154

def handle_stop
  Thread.start { sleep 0.1; @node.stop }
  {:state => "Stopping..."}
end

#logObject

wrap logger and append prefix



17
18
19
# File 'lib/bitcoin/network/command_handler.rb', line 17

def log
  @log ||= Bitcoin::Logger::LogWrapper.new("command:", @node.log)
end

#receive_data(data) ⇒ Object

receive request from the client



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/bitcoin/network/command_handler.rb', line 30

def receive_data data
  @buf.extract(data).each do |packet|
    cmd, args = JSON::parse(packet)
    log.debug { [cmd, args] }
    if respond_to?("handle_#{cmd}")
      respond(cmd, send("handle_#{cmd}", *args))
    else
      respond(cmd, {:error => "unknown command: #{cmd}. send 'help' for help."})
    end
  end
rescue Exception
  p $!
end

#respond(cmd, data) ⇒ Object

respond to a command; send serialized response to the client



22
23
24
25
26
27
# File 'lib/bitcoin/network/command_handler.rb', line 22

def respond(cmd, data)
  return  unless data
  @lock.synchronize do
    send_data([cmd, data].to_json + "\x00")
  end
end

#unbindObject

disconnect notification clients when connection is closed



174
175
176
177
# File 'lib/bitcoin/network/command_handler.rb', line 174

def unbind
  #@node.notifiers.unsubscribe(@notify_sid)  if @notify_sid
  @node.command_connections.delete(self)
end