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:
Nicholas Thomas
2011-05-15 00:36:57 +01:00
commit 5554ff2dbe
17 changed files with 321 additions and 0 deletions

0
Rakefile Normal file
View File

22
bin/sharp-coin Executable file
View 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
View 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
View 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
View 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
View 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
View 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

View File

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View File