Skip to content

Instantly share code, notes, and snippets.

@Abstrct
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Abstrct/15ade941d987a833a2bb to your computer and use it in GitHub Desktop.
Save Abstrct/15ade941d987a833a2bb to your computer and use it in GitHub Desktop.
=begin
WARNING - This has some issues with really small numbers at the moment. I think this goes without saying
for random gists you find on the internet, but you probably shouldn't use this in production.
For every altcoin you want to store, create this schema:
CREATE SCHEMA fuckcoin;
CREATE TABLE fuckcoin.block (
hash character varying PRIMARY KEY,
details json
);
CREATE TABLE fuckcoin.transaction (
hash character varying PRIMARY KEY,
block_hash character varying,
details json,
status character varying
);
Todo:
Validate
Missing Blocks
Incomplete Blocks (Missing Transactions)
=end
require 'net/http'
require 'uri'
require 'json'
require 'pg'
$rpc_url = 'http://user:password@127.0.0.1:1338'
$db_url = 'host=127.0.0.1 dbname=pgcoin'
# This number should not be higher than the number of connections allowed by postgres
$max_threads = 90
# This class is just a copy from the Bitcoin class shared here: https://en.bitcoin.it/wiki/API_reference_(JSON-RPC)
class CoinRPC
def initialize(service_url)
@uri = URI.parse(service_url)
end
def method_missing(name, *args)
post_body = { 'method' => name, 'params' => args, 'id' => 'jsonrpc' }.to_json
resp = JSON.parse( http_post_request(post_body) )
raise JSONRPCError, resp['error'] if resp['error']
resp['result']
end
def http_post_request(post_body)
http = Net::HTTP.new(@uri.host, @uri.port)
request = Net::HTTP::Post.new(@uri.request_uri)
request.basic_auth @uri.user, @uri.password
request.content_type = 'application/json'
request.body = post_body
http.request(request).body
end
class JSONRPCError < RuntimeError; end
end
def insertBlock block
conn = PG.connect($db_url)
conn.prepare('InsertBlock', 'insert into fuckcoin.block (hash, details) values ($1, $2)')
conn.prepare('InsertGenesisTX', 'insert into fuckcoin.transaction (hash, block_hash) values ($1, $2)')
conn.prepare('InsertTX', 'insert into fuckcoin.transaction (hash, block_hash, details) values ($1, $2, $3)')
conn.exec_prepared('InsertBlock', [ block["hash"], JSON.generate(block) ])
block["tx"].each do |tx_hash|
if block["height"] == 0
conn.exec_prepared('InsertGenesisTX', [ tx_hash, block["hash"] ])
else
t = CoinRPC.new($rpc_url)
transaction = t.decoderawtransaction(t.getrawtransaction(tx_hash))
conn.exec_prepared('InsertTX', [ transaction["txid"], block["hash"], JSON.generate(transaction) ])
end
end
conn.finish
Thread.exit
end
def getLastSyncedBlock
conn = PG.connect($db_url)
res = conn.exec('SELECT details->>\'hash\' as h from fuckcoin.block order by (details->>\'height\')::integer DESC LIMIT 1 ')
unless res.cmd_tuples() == 0
lastBlockHash = res[0]['h']
end
conn.finish
lastBlockHash
end
if $0 == __FILE__
h = CoinRPC.new($rpc_url)
p 'Checking for last stored block'
startingBlock = getLastSyncedBlock()
if startingBlock.nil?
p 'Could not find a block, starting at the Genesis'
block = h.getblock(h.getblockhash(0))
p block["hash"]
Thread.new{insertBlock(block)}
else
p startingBlock
block = h.getblock(startingBlock)
p 'Next Hash Found ' + block["hash"]
end
nextBlockHash = block["nextblockhash"]
while true
if Thread.list.count < $max_threads
begin
while nextBlockHash.nil?
p "Up to date, sleeping"
p block["hash"]
sleep 60
block = h.getblock(block["hash"])
nextBlockHash = block["nextblockhash"]
end
block = h.getblock(nextBlockHash)
nextBlockHash = block["nextblockhash"]
Thread.new{insertBlock(block)}
rescue
p nextBlockHash
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment