Starting point
None of this code is final - indeed, most of it is just gubbins - but it shows the path I mean to take with the code, I hope. Much more to come.
This commit is contained in:
22
bin/sharp-coin
Executable file
22
bin/sharp-coin
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$: << 'lib' # TODO: remove this
|
||||
|
||||
require 'sharp-coin'
|
||||
|
||||
SharpCoin::Config::read(ARGV[0])
|
||||
|
||||
server = SharpCoin::Server.new
|
||||
|
||||
trap("INT") do
|
||||
server.stop
|
||||
end
|
||||
|
||||
trap("KILL") do
|
||||
server.stop
|
||||
end
|
||||
|
||||
EM::run do
|
||||
EventMachine.epoll
|
||||
server.run
|
||||
end
|
41
doc/AIMS
Normal file
41
doc/AIMS
Normal file
@@ -0,0 +1,41 @@
|
||||
=== To produce a bitcoin application ===
|
||||
|
||||
This will be a web application, written in ruby (using appropriate technologies)
|
||||
that can be run standalone, for a single user on their home machine, or on a web
|
||||
server for many users.
|
||||
|
||||
Needs to be feature-complete by comparison to the official bitcoin client, with
|
||||
the exception of block generation - we're not bothering with that aspect at all
|
||||
right now. Also needs feature parity with mybitcoin.com
|
||||
|
||||
List:
|
||||
- Multiple users
|
||||
- Each user has a wallet
|
||||
- Ability to receive payments
|
||||
- Transaction history
|
||||
- Ability to make payments, specify fee per-payment
|
||||
- Able to take part in the global bitcoin network as a full member.
|
||||
- Shopping cart integration
|
||||
- On-the-fly currency comparison
|
||||
- Payment forwarding
|
||||
- API access via the JSON-RPC specification to control the account
|
||||
|
||||
Also needs *extra* features
|
||||
|
||||
- Only one block chain for all users in the cluster (faster startup time)
|
||||
- (mybitcoin) : open-source, run it for yourself if you want to!
|
||||
- britcoin, bitmarket, mtgox integration
|
||||
- Address book
|
||||
- Support for sending payments to email addresses - use webfinger to resolve
|
||||
- Simpler REST API
|
||||
|
||||
=== Technology ===
|
||||
|
||||
Runtime: Ruby 1.9.2-p180
|
||||
Web server: Thin
|
||||
Templating: haml, sass, coffeescript
|
||||
ORM: ActiveRecord
|
||||
Live action: SSE (depends: thin-async)
|
||||
Key generation: OpenSSL
|
||||
Database: standalone: SQLite3/SQLCipher. All-in: postgres/mysql
|
||||
Bitcoin network peer: event-machine, bit-struct
|
14
lib/em-bitcoin.rb
Normal file
14
lib/em-bitcoin.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
require 'eventmachine'
|
||||
|
||||
module EventMachine
|
||||
module Protocols
|
||||
# Implements the TCP protocol that Bitcoin peers speak to each other. This
|
||||
# class can be used for both incoming and outgoing connections
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class BitcoinPeer < EventMachine::Connection
|
||||
# TODO!
|
||||
def receive_data(data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
5
lib/sharp-coin.rb
Normal file
5
lib/sharp-coin.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
require 'sharp-coin/config'
|
||||
require 'sharp-coin/db'
|
||||
require 'sharp-coin/logging'
|
||||
require 'sharp-coin/server'
|
||||
require 'sharp-coin/interface'
|
51
lib/sharp-coin/config.rb
Normal file
51
lib/sharp-coin/config.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
require 'yaml'
|
||||
require 'blankslate'
|
||||
|
||||
module SharpCoin
|
||||
# Parses the sharp-coin.config.yaml file and provides access to all its
|
||||
# settings in one place. This is a singleton.
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class Config < BlankSlate
|
||||
def initialize
|
||||
raise ArgumentError.new("Singleton class!")
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
# Load the configuration file into memory.
|
||||
# @param[String] filename Configuration file
|
||||
# @return[SharpCoin::Config] self
|
||||
def read(filename)
|
||||
@config = YAML::parse_file(filename)
|
||||
end
|
||||
|
||||
def http_host
|
||||
"localhost"
|
||||
end
|
||||
|
||||
def http_port
|
||||
3000
|
||||
end
|
||||
|
||||
# @return[Array<String,Fixnum>] Address and port that the HTTP server
|
||||
# should bind to.
|
||||
def http_bind
|
||||
[http_host, http_port]
|
||||
end
|
||||
|
||||
def telnet_host
|
||||
"localhost"
|
||||
end
|
||||
|
||||
def telnet_port
|
||||
3001
|
||||
end
|
||||
|
||||
def telnet_bind
|
||||
[telnet_host, telnet_port]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
14
lib/sharp-coin/db.rb
Normal file
14
lib/sharp-coin/db.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
gem 'activerecord', '3.0.7' # FIXME: Ugh
|
||||
|
||||
require 'active_record'
|
||||
|
||||
module SharpCoin
|
||||
module DB
|
||||
class << self
|
||||
def setup!
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
0
lib/sharp-coin/db/schema.rb
Normal file
0
lib/sharp-coin/db/schema.rb
Normal file
13
lib/sharp-coin/interface.rb
Normal file
13
lib/sharp-coin/interface.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
require 'sharp-coin/interface/json-rpc'
|
||||
require 'sharp-coin/interface/rest'
|
||||
require 'sharp-coin/interface/telnet'
|
||||
require 'sharp-coin/interface/web'
|
||||
|
||||
module SharpCoin
|
||||
# Hooks to start and stop the various interfaces that the
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
module Interface
|
||||
|
||||
end
|
||||
|
||||
end
|
9
lib/sharp-coin/interface/bitcoin.rb
Normal file
9
lib/sharp-coin/interface/bitcoin.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module SharpCoin
|
||||
module Interface
|
||||
# Handle communications with other BitCoin peers. We rely on
|
||||
# EM::P::BitcoinPeer for much of the heavy lifting.
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class Bitcoin
|
||||
end
|
||||
end
|
||||
end
|
15
lib/sharp-coin/interface/json-rpc.rb
Normal file
15
lib/sharp-coin/interface/json-rpc.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'sinatra/base'
|
||||
module SharpCoin
|
||||
module Interface
|
||||
|
||||
# JSON-RPC API for SharpCoin. Used by legacy clients to access accounts
|
||||
# and do clever things.
|
||||
#
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class JsonRpc < Sinatra::Base
|
||||
end
|
||||
JsonRPC = JsonRpc
|
||||
JSONRPC = JsonRpc
|
||||
end
|
||||
end
|
||||
|
14
lib/sharp-coin/interface/rest.rb
Normal file
14
lib/sharp-coin/interface/rest.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
require 'sinatra/base'
|
||||
module SharpCoin
|
||||
module Interface
|
||||
|
||||
# REST API for SharpCoin. Used by the web interface and clever clients to
|
||||
# do things.
|
||||
#
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class Rest < Sinatra::Base
|
||||
end
|
||||
REST = Rest
|
||||
end
|
||||
end
|
||||
|
15
lib/sharp-coin/interface/telnet.rb
Normal file
15
lib/sharp-coin/interface/telnet.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'em/protocols/line_and_text'
|
||||
|
||||
module SharpCoin
|
||||
module Interface
|
||||
|
||||
# Telnet API for SharpCoin. Used for debugging and administrative tasks
|
||||
#
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class Telnet < EM::P::LineAndTextProtocol
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
26
lib/sharp-coin/interface/web.rb
Normal file
26
lib/sharp-coin/interface/web.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
require 'sinatra/base'
|
||||
require 'sharp-coin/interface/rest'
|
||||
|
||||
module SharpCoin
|
||||
module Interface
|
||||
|
||||
# Human-usable web application for SharpCoin. Also mediates access to the
|
||||
# two API end-points (JSON-RPC and REST). Is basically a skinny server for
|
||||
# the templates found in sharp-coin/tmpl, which form the web application.
|
||||
# Most of the web application work is actually done by talking to the
|
||||
# REST API!
|
||||
#
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class WebReal < Sinatra::Base
|
||||
|
||||
end
|
||||
|
||||
Web = Rack::Builder.new do
|
||||
map("/api/rpc/") { run JsonRPC }
|
||||
map("/api/rest/") { run REST }
|
||||
map("/") { run WebReal }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
41
lib/sharp-coin/logging.rb
Normal file
41
lib/sharp-coin/logging.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require 'log4r'
|
||||
require 'blankslate'
|
||||
|
||||
module SharpCoin
|
||||
# If an object doesn't have a logger, we 'log' to this instead.
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class DummyLogger < BlankSlate
|
||||
def method_missing(*args)
|
||||
STDERR.puts("DummyLogger: #{args.inspect}")
|
||||
end
|
||||
end
|
||||
|
||||
# Include this in any class where you want logging to go to a single place.
|
||||
# Controlled by SharpCoin::Config, obviously.
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
module Logging
|
||||
class << self
|
||||
def logger
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def logger
|
||||
@logger || self.class.logger || DummyLogger.new
|
||||
end
|
||||
|
||||
# @param[Array[Symbol,String]|Array[String]] Data to log. Can specify a log
|
||||
# level and a message, or just a message. If the latter, then we default
|
||||
# to info
|
||||
def log(*args)
|
||||
case args.size
|
||||
when 1 then logger.info(args[0])
|
||||
when 2 then logger.send(*args)
|
||||
else
|
||||
logger.warn("Bad method signature for log. Args: #{args.inspect}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
41
lib/sharp-coin/server.rb
Normal file
41
lib/sharp-coin/server.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require 'eventmachine'
|
||||
require 'thin'
|
||||
|
||||
require 'sharp-coin/db'
|
||||
require 'sharp-coin/interface'
|
||||
module SharpCoin
|
||||
# Beating heart of the SharpCoin application. Sets up all the components
|
||||
# according to the config, handles all the events as needed.
|
||||
# @author Nick Thomas <nick@lupine.me.uk>
|
||||
class Server
|
||||
include Logging
|
||||
|
||||
# Create a new server instance. This instance coordinates the various bits
|
||||
# of SharpCoin to produce a working application.
|
||||
# @param[
|
||||
def initialize
|
||||
DB::setup!
|
||||
end
|
||||
|
||||
# Start the various services off. This should generally be called inside an
|
||||
# EM::run { ... } block
|
||||
def run
|
||||
@running = true
|
||||
@thin = Thin::Server.start(Interface::Web, *(Config::http_bind))
|
||||
@telnet = EM::start_server(*([Config::telnet_bind, Interface::Telnet].flatten))
|
||||
end
|
||||
|
||||
# @return[Boolean] Is this server instance currently running?
|
||||
def running?
|
||||
@running == true
|
||||
end
|
||||
|
||||
# Stop the various services.
|
||||
def stop
|
||||
@thin.stop
|
||||
@telnet.stop
|
||||
@running = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
0
sharp-coin.config.yaml
Normal file
0
sharp-coin.config.yaml
Normal file
Reference in New Issue
Block a user