117 lines
3.4 KiB
Ruby
117 lines
3.4 KiB
Ruby
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 <nick@lupine.me.uk>
|
|
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 <nick@lupine.me.uk>
|
|
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 <nick@lupine.me.uk>
|
|
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
|