Initial commit
This commit is contained in:
commit
14e656379c
105
namekrs.py
Normal file
105
namekrs.py
Normal file
@ -0,0 +1,105 @@
|
||||
import datetime
|
||||
import json
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
|
||||
from timestamper import rpcurl_from_config
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(levelname)s] %(message)s')
|
||||
|
||||
class NamecoinStore(object):
|
||||
identity = None
|
||||
|
||||
def __init__(self, url='http://127.0.0.1:8336', identity=None):
|
||||
self.client = AuthServiceProxy(url)
|
||||
self.identity = identity
|
||||
|
||||
def set(self, key, value):
|
||||
name = self.identity.format(key)
|
||||
data = json.dumps(value)
|
||||
|
||||
reg_txid, nonce = self.client.name_new(name)
|
||||
logging.debug('reg_txid, nonce: %r %r', reg_txid, nonce)
|
||||
txid = self.client.name_firstupdate(name, nonce, reg_txid, data)
|
||||
logging.debug('txid: %r', txid)
|
||||
return txid
|
||||
|
||||
def get(self, key):
|
||||
try:
|
||||
hist = self.client.name_history(self.identity.format(key))
|
||||
except JSONRPCException as exc:
|
||||
# FIXME maybe?
|
||||
if str(exc).split(': ')[0] == '-4':
|
||||
logging.exception('No record found')
|
||||
return None
|
||||
|
||||
raise
|
||||
|
||||
last_tx = self.client.gettransaction(hist[-1]['txid'])
|
||||
|
||||
return {
|
||||
'history': hist,
|
||||
'last_tx': last_tx
|
||||
}
|
||||
|
||||
def bind_parser(p):
|
||||
def deco(func):
|
||||
p.set_defaults(func=func)
|
||||
return func
|
||||
return deco
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--rpc', help='namecoind RPC URL (defaults to data obtained from ~/.namecoin.conf)')
|
||||
sub = parser.add_subparsers()
|
||||
|
||||
verify_parser = sub.add_parser('verify', help='lookup KRS database')
|
||||
verify_parser.add_argument('id', help='lookup ID')
|
||||
|
||||
@bind_parser(verify_parser)
|
||||
def verify(args, store):
|
||||
print(store.get(args.id))
|
||||
|
||||
publish_parser = sub.add_parser('publish', help='publish mojepanstwo.pl data')
|
||||
publish_parser.add_argument('--limit', type=int, help='limit number of records', default=20)
|
||||
publish_parser.add_argument('--page', type=int, help='start with specified page', default=1)
|
||||
publish_parser.add_argument('-n', '--dry-run', action='store_true', help='only parse data')
|
||||
|
||||
@bind_parser(publish_parser)
|
||||
def publish(args, store):
|
||||
output = {}
|
||||
|
||||
data = requests.get('https://api-v3.mojepanstwo.pl/dane/krs_podmioty.json', {
|
||||
'limit': args.limit,
|
||||
'page': args.page
|
||||
}).json()
|
||||
logging.info('Total objects: %d, selected: %d', data['Count'], len(data['Dataobject']))
|
||||
|
||||
for obj in data['Dataobject']:
|
||||
if obj['data']['krs_podmioty.nip'] == '0':
|
||||
logging.info('Ignoring %s (no NIP/VATID)', obj['mp_url'])
|
||||
continue
|
||||
k = obj['data']['krs_podmioty.nip']
|
||||
|
||||
output[k] = obj['mp_url']
|
||||
logging.info('%s => %r', k, output[k])
|
||||
|
||||
if args.dry_run:
|
||||
logging.info('Dry run, not publishing')
|
||||
return
|
||||
|
||||
for k, v in output.items():
|
||||
logging.info('Publishing %s => %r', k, v)
|
||||
store.set(k, v)
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
store = NamecoinStore(
|
||||
args.rpc or rpcurl_from_config('namecoin', ''),
|
||||
'krs-test/{}')
|
||||
args.func(args, store)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
156
timestamper.py
Normal file
156
timestamper.py
Normal file
@ -0,0 +1,156 @@
|
||||
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
|
||||
from datetime import datetime
|
||||
import json
|
||||
import hashlib
|
||||
import sys
|
||||
import platform
|
||||
import os
|
||||
|
||||
|
||||
class Timestamper(object):
|
||||
"""Basic timestamping interface"""
|
||||
|
||||
def verify(self, digest):
|
||||
"""Checks if digest (hash) is present in selected blockchain and returns
|
||||
its timestamp and (unique) transaction hash/id in which it has been
|
||||
announced. Returns None if it has not been found."""
|
||||
raise NotImplemented
|
||||
|
||||
def publish(self, digest):
|
||||
"""Publishes digest onto selected blockchain and returns transaction
|
||||
hash/id (which should match with one reported by verify call)"""
|
||||
raise NotImplemented
|
||||
|
||||
def hash_file(self, fd):
|
||||
"""Returns sha256 hash of provided open fd."""
|
||||
|
||||
filehash = hashlib.sha256()
|
||||
|
||||
for chunk in iter(lambda: fd.read(8192), b""):
|
||||
filehash.update(chunk)
|
||||
|
||||
return filehash.hexdigest()
|
||||
|
||||
def verify_file(self, fd):
|
||||
"""Verifies file from open fd using sha256."""
|
||||
|
||||
return self.verify(self.hash_file(fd))
|
||||
|
||||
def publish_file(self, fd):
|
||||
"""Publishes file from open fd using sha256."""
|
||||
|
||||
return self.publish(self.hash_file(fd))
|
||||
|
||||
|
||||
class NamecoinTimestamper(Timestamper):
|
||||
"""Simple Namecoin proof-of-existence timestamper implementation based on
|
||||
poe/ identity prefix.
|
||||
"""
|
||||
|
||||
IDENTITY = 'poe/{}'
|
||||
|
||||
def __init__(self, url='http://127.0.0.1:8336'):
|
||||
self.client = AuthServiceProxy(url)
|
||||
|
||||
def verify(self, digest):
|
||||
"""Namecoin poe/ verification implementation"""
|
||||
|
||||
try:
|
||||
hist = self.client.name_history(self.IDENTITY.format(digest))
|
||||
except JSONRPCException as exc:
|
||||
# FIXME maybe?
|
||||
if str(exc).split(': ')[0] == '-4':
|
||||
return None
|
||||
|
||||
raise
|
||||
|
||||
txid = hist[0]['txid']
|
||||
tx = self.client.gettransaction(txid)
|
||||
|
||||
return {
|
||||
'txid': txid,
|
||||
'timestamp': datetime.utcfromtimestamp(tx['time']),
|
||||
}
|
||||
|
||||
def publish(self, digest):
|
||||
"""Namecoin poe/ publishing implementation"""
|
||||
|
||||
name = self.IDENTITY.format(digest)
|
||||
reg_txid, nonce = self.client.name_new(name)
|
||||
|
||||
txid = self.client.name_firstupdate(name, nonce, reg_txid, json.dumps({
|
||||
'ver': 0,
|
||||
}))
|
||||
|
||||
return txid
|
||||
|
||||
def parse_bitcoin_conf(fd):
|
||||
"""Returns dict from bitcoin.conf-like configuration file from open fd"""
|
||||
conf = {}
|
||||
for l in fd:
|
||||
l = l.strip()
|
||||
if not l.startswith('#') and '=' in l:
|
||||
key, value = l.split('=', 1)
|
||||
conf[key] = value
|
||||
|
||||
return conf
|
||||
|
||||
def coin_config_path(coin):
|
||||
"""Returns bitcoin.conf-like configuration path for provided coin"""
|
||||
|
||||
paths = {
|
||||
# FIXME use proper AppData path
|
||||
'Windows': '~\AppData\Roaming\{0}\{0}.conf',
|
||||
'Darwin': '~/Library/Application Support/{0}/{0}.conf',
|
||||
|
||||
# Fallback path (Linux, FreeBSD...)
|
||||
None: '~/.{0}/{0}.conf',
|
||||
}
|
||||
|
||||
path = paths.get(platform.system(), paths[None])
|
||||
|
||||
return os.path.expanduser(path.format(coin))
|
||||
|
||||
def rpcurl_from_config(coin, default=None, config_path=None):
|
||||
"""Returns RPC URL loaded from bitcoin.conf-like configuration of desired
|
||||
currency"""
|
||||
|
||||
config_path = config_path or coin_config_path(coin)
|
||||
cookie_path = os.path.join(os.path.dirname(config_path), '.cookie')
|
||||
|
||||
credentials = ''
|
||||
|
||||
try:
|
||||
with open(config_path) as fd:
|
||||
conf = parse_bitcoin_conf(fd)
|
||||
if 'rpcpassword' in conf:
|
||||
# Password authentication
|
||||
credentials = '{rpcuser}:{rpcpassword}'.format(**conf)
|
||||
elif os.path.exists(cookie_path):
|
||||
# Cookie authentication
|
||||
with open(cookie_path) as cfd:
|
||||
credentials = cfd.read().decode('utf-8').strip() \
|
||||
.replace('/', '%2F')
|
||||
else:
|
||||
return default
|
||||
|
||||
return 'http://{0}@127.0.0.1:{1}/' \
|
||||
.format(credentials, conf.get('rpcport', 8336))
|
||||
except:
|
||||
return default
|
||||
|
||||
def main():
|
||||
ts = NamecoinTimestamper(rpcurl_from_config('namecoin', 'http://127.0.0.1:8336/'))
|
||||
|
||||
for f in sys.argv[1:]:
|
||||
with open(f) as fd:
|
||||
timestamp = ts.verify_file(fd)
|
||||
|
||||
if timestamp:
|
||||
print('File {} found at: {}'.format(f, timestamp['timestamp']))
|
||||
else:
|
||||
print('File {} not found'.format(f))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user