Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dagurval committed Mar 16, 2018
0 parents commit f7ede42
Show file tree
Hide file tree
Showing 13 changed files with 745 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
@@ -0,0 +1,16 @@
# Update vote information

- Requires a Bitcoin XT node running with this patch https://github.com/dgenr8/bitcoinxt/commit/62ec4ca3bba718668f0aab1dc5ff570f9441092f
- Node must be configured with txindex=1
- RPC username and password are assumed to be located at ../.bitcoin/bitcoin.conf

Depends on pygtail, jinja2 and simplejson python libraries.

On ubuntu:
sudo apt-get install python3-pygtail, python3-jinja2, python3-simplejson

Run doublespend.py to generate double spend data and update the static website.

# The website
The website itself consists of only static files. Point the webserver to serve files in folder site/

140 changes: 140 additions & 0 deletions debug-sample.log

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions doublespend.py
@@ -0,0 +1,40 @@
import sys
import time
from respend.logparser import RespendChecker
from respend.txdecoder import decode_tx
from respend.blockchain import BlockchainChecker
from respend.winnerupdater import update_winners
from respend.txdata import store_respend
from respend.websitebuilder import build_website

TXDATA = "txdata"

def on_respend_detected(tx1, tx2):
tx1_decoded = decode_tx(tx1.hex)
tx1_decoded['first_seen'] = tx1.time
tx2_decoded = decode_tx(tx2.hex)
tx2_decoded['first_seen'] = tx2.time

print("Wrote %s" % store_respend({
'first' : tx1_decoded,
'second' : tx2_decoded,
'timestamp' : tx1.time,
'winner' : None}))

respend = RespendChecker(
logpath = "debug-sample.log",
on_respend = on_respend_detected)

blockchain = BlockchainChecker(
on_new_tip = lambda: update_winners())

while True:
update_website = respend.check()
update_website |= blockchain.check()
if update_website:
print("New changes. Updating website.")
build_website()

sys.stdout.write(".")
sys.stdout.flush()
time.sleep(1)
Empty file added respend/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions respend/blockchain.py
@@ -0,0 +1,14 @@
from respend.rpcutil import connection

class BlockchainChecker:
def __init__(self, on_new_tip):
self.on_new_tip = on_new_tip
self.tip = None

def check(self):
new_tip = connection().getbestblockhash()
if new_tip == self.tip:
return False
print("New blockchain tip: %s" % new_tip)
self.tip = new_tip
return self.on_new_tip()
65 changes: 65 additions & 0 deletions respend/logparser.py
@@ -0,0 +1,65 @@
import re
from pygtail import Pygtail

def regex_match(regex, line):
m = re.match(regex, line)
if m is None:
return m
return m.group(1);

class RespendTx:
def __init__(self, time_regex, hex_regex):
self.time_regex = time_regex
self.hex_regex = hex_regex

self.time = None
self.hex = None

def parse_line(self, line):
t = regex_match(self.time_regex, line)
if t is not None:
self.time = t
print("matched %s %s" % (self.time_regex, line))

h = regex_match(self.hex_regex, line)
if h is not None:
self.hex = h
print("matched %s %s" % (self.hex_regex, line))

def done(self):
return self.time != None and self.hex != None

def clear(self):
self.time = self.hex = None

class RespendChecker():
def __init__(self, logpath, on_respend):
self.logpath = logpath
self.on_respend = on_respend

def check(self):
tx1 = RespendTx(
time_regex = r'.*tx1: (\d{4}-\d{2}-\d{2} [0-9:]{8})',
hex_regex = r'.*tx1 hex: ([0-9a-f]+)')

tx2 = RespendTx(
time_regex = r'(\d{4}-\d{2}-\d{2} [0-9:]{8}) Respend tx2',
hex_regex = r'.*tx2 hex: ([0-9a-f]+)')

found = False
for line in Pygtail(self.logpath):
# tx2 is logged before tx1

if not tx2.done():
tx2.parse_line(line)

elif not tx1.done():
tx1.parse_line(line)

if tx1.done() and tx2.done():
print("found double spend")
self.on_respend(tx1, tx2)
tx1.clear()
tx2.clear()
found = True
return found
26 changes: 26 additions & 0 deletions respend/rpcutil.py
@@ -0,0 +1,26 @@
import sys
sys.path.append("./lib/bitcoinrpc")
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from functools import lru_cache

def read_cfg(param):
cfglines = open("../.bitcoin/bitcoin.conf").readlines()
for c in cfglines:
if c.startswith(param):
return c[len(param) + 1:-1]
return None

rpcuser = read_cfg("rpcuser")
rpcpassword = read_cfg("rpcpassword")
port = 8332

url = "http://%s:%s@127.0.0.1:%s" % (rpcuser, rpcpassword, port)
conn = AuthServiceProxy(url)

def connection():
global conn
return conn

@lru_cache(maxsize = 1024)
def get_cached_tx(txid):
return connection().getrawtransaction(txid, 1)
28 changes: 28 additions & 0 deletions respend/txdata.py
@@ -0,0 +1,28 @@
import simplejson as json
import hashlib
import glob
import os

TXDATA = "txdata"

def has_winner(respend):
return respend['winner'] != None

def list_respends():
return glob.glob(TXDATA + "/*.json")

def load_respend(path):
with open(path) as fh:
return json.load(fh)

def respend_id(txid_first, txid_second):
h = hashlib.new('ripemd160')
h.update(txid_first.encode() + txid_second.encode())
return h.hexdigest()

def store_respend(respend):
rid = respend_id(respend['first']['txid'], respend['second']['txid'])
path = os.path.join(TXDATA, rid + ".json")
with open(path, "w") as fh:
json.dump(respend, fh, sort_keys = True, indent = 2 * ' ')
return path
4 changes: 4 additions & 0 deletions respend/txdecoder.py
@@ -0,0 +1,4 @@
from respend.rpcutil import connection

def decode_tx(hexstr):
return connection().decoderawtransaction(hexstr)

0 comments on commit f7ede42

Please sign in to comment.