require 'eventmachine' require 'btc_wire_proto' module EventMachine module Protocols # Implements the TCP protocol that Bitcoin peers speak to each other. This # module is mixed into both incoming and outgoing connections. # # We implement the protocol as a simple(ish!) state machine. When we want # something doing, we call state(sym, data) to append that to the # list of things to do. If something is urgent, we can call state! to # put it at the beginning of the list. # # Here is a list of states: # send_ver, recv_ver, verify_ver # send_verack, recv_verack # wait, finish # # Documentation is here: https://en.bitcoin.it/wiki/Network # # @author Nick Thomas module BitcoinPeer protected # Sets up the variables required to manage the state machine. Should be # called before you try to push a state - in post_init, say. def init_state! @state_m = Mutex.new # Synchronize around @states and @working @state_m.synchronize do @states = [] @working = false end end # Checks the current configuration object to see if we have a valid config # or not. # @return[Array[true|false, msg]] Whether the config is valid, and an # optional message specifying why it's invalid, if it is. def valid_config? [false, "configuration check not implemented yet"] end # Push a state to the end of the state queue. def state(new_state, data = nil) @state_m.synchronize { @states.push(new_state, data) } end # Add a state to the start of the state queue. def state!(new_state, data = nil) @state_m.synchronize { @states.unshift(new_state, data) } end # State machine behaviours now. # Send a 'version' message to the peer. # Next def send_ver end end # EventMachine protocol class that handles an *outgoing* connection to # another bitcoin peer. Common functionality (p2p!) is held in BitcoinPeer. # # State machine flow: # send_ver, recv_verack # recv_ver, verify_ver, send_verack # # @author Nick Thomas class BitcoinClient < EM::Connection include BitcoinPeer # @param[Object] config See the BitcoinPeer#valid_config? def initialize(config) super @config = config result, msg = valid_config? raise ArgumentError.new("Invalid configuration: #{msg}") unless result init_state! end def post_init state(:send_ver) end end # EventMachine protocol class that handles an *incoming* connection from # another bitcoin peer. Common functionality (p2p!) is held in BitcoinPeer # # State machine flow: # recv_ver, verify_ver, send_verack # send_ver, recv_verack # # @author Nick Thomas class BitcoinServer < EM::Connection include BitcoinPeer # @param[Object] config See the BitcoinPeer#valid_config? def initialize(config) super @config = config result, msg = valid_config? raise ArgumentError.new("Invalid configuration: #{msg}") unless result init_state! end def post_init state(:recv_ver) end end end end