Last active
December 15, 2015 09:59
-
-
Save lsparrish/5242810 to your computer and use it in GitHub Desktop.
simple, vaguely bitcoin-like program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #==========Bitbrick: a somewhat bitcoin-like program.==========# | |
| # The goal is secure published transactions. Not yet completed. | |
| # So far we have the ability to ensure that a given address has | |
| # enough funds to send based on the sum of past transactions. | |
| # Install Notes: | |
| # Uses dbdict, a recipe from: http://code.activestate.com/recipes/576642-persistent-dict-with-multiple-standard-file-format/ | |
| # On Ubuntu the Crypto library can be installed with "sudo apt-get install python-crypto" | |
| # Details: | |
| # Addresses are first 16 chars of a hash of the public key. | |
| # Transaction IDs are first 16 chars of the buyer's signature. | |
| # Receipts are lists of debits and credits that have been applied to each address. | |
| # Buyer is sender, seller is receiver. (May also be donor/recipient.) | |
| # Todo: | |
| # Needs to share transactions, with common starting points (like a genesis block) and so forth. | |
| # Should be able to be infinitely divisible by using multipliers. | |
| # Idea is to make many borrowable fungible currencies that float with | |
| # relation next to each other, with bitbricks acting as the finite-supply | |
| # stable intermediary. There needs to be clean ways to convert/recycle between them. | |
| # Stored needs to be indexed to proof of work. | |
| #===============================# | |
| from Crypto.Hash import SHA256 | |
| from Crypto.PublicKey import RSA | |
| from Crypto import Random | |
| from time import ctime | |
| from random import random | |
| from dbdict import PersistentDict | |
| from pprint import pprint | |
| #===============================# | |
| def r(): return Random.new().read | |
| def k(): return RSA.generate(1024,r()) | |
| def newitem(): | |
| key=k(); pkey=key.publickey() | |
| text=pkey.exportKey() | |
| return SHA256.new(text).hexdigest()[:16], key, pkey | |
| def privateaddress(address, key, pkey): | |
| return {address:key.exportKey()} | |
| def publicaddress(address, key, pkey): | |
| return {address:pkey.exportKey()} | |
| def newaddress(d,item): | |
| d['private'].update(privateaddress(*item)) | |
| d['public'].update(publicaddress(*item)) | |
| d['receipt']['debit'].update({item[0]:[]}) | |
| d['receipt']['credit'].update({item[0]:[]}) | |
| d['stored'].update({item[0]:0}) | |
| def newreciept(transid): | |
| buyer,seller,amount=transinfo(transid)[:3] | |
| d['receipt']['credit'][seller].append(transid) | |
| d['receipt']['debit'][buyer].append(transid) | |
| #---lookup functions---# | |
| def getpkey(address): | |
| text=d['public'][address] | |
| return RSA.importKey(text) | |
| def getkey(address): | |
| text=d['private'][address] | |
| return RSA.importKey(text) | |
| def transinfo(transid): | |
| return d['transaction'][transid][1] | |
| def transbuyer(transid): | |
| return transinfo(transid)[0] | |
| def transseller(transid): | |
| return transinfo(transid)[1] | |
| def transamount(transid): | |
| return transinfo(transid)[2] | |
| #---transaction functions---# | |
| def createtransaction(buyer,seller,amount): | |
| key=getkey(buyer) | |
| transinfo=[buyer,seller,amount,ctime()] | |
| transtext=str(transinfo) | |
| transsig=key.sign(transtext,'') | |
| transid=str(transsig[0])[:16] | |
| return {transid:[transsig,transinfo]} | |
| def addtransaction(buyer,seller,amount): | |
| trans=createtransaction(buyer,seller,amount) | |
| d['transaction'].update(trans) | |
| transid=trans.keys()[0] | |
| newreciept(transid) | |
| return transid | |
| def verifytranssig(transid): | |
| trans=d['transaction'][transid] | |
| transsig=trans[0] | |
| transinfo=trans[1] | |
| pkey=getpkey(transinfo[0]) | |
| return pkey.verify(str(transinfo),transsig) | |
| def getstored(address): | |
| amountstored=d['stored'][address] | |
| print "Buyer Address: " + address + " Amount Stored: " + str(amountstored) | |
| return amountstored | |
| def getamounts(address,transtype): | |
| translist=d['receipt'][transtype][address] | |
| total=[] | |
| for transid in translist: | |
| total.append(transamount(transid)) | |
| print "Buyer Address: " + str(address) + " Transaction: " + str(transtype) + " Total: " + str(sum(total)) | |
| return sum(total) | |
| def verifytransamount(transid): | |
| available=getamounts(transbuyer(transid),'credit')-getamounts(transbuyer(transid),'debit')+getstored(transbuyer(transid))+transamount(transid) | |
| print "Available for transfer: " + str(available) | |
| return available >= transamount(transid) | |
| def verifytransaction(transid): | |
| return verifytranssig(transid) & verifytransamount(transid) | |
| #---publish---# | |
| def sendtrans(transid): | |
| #submit d['transaction'][transid] | |
| #print transinfo(transid) # placeholder | |
| print "Network functionality not implemented yet. Transaction kept locally." | |
| def publishtrans(transid): | |
| # filter function, only sends it out if available is greater than trans amount | |
| if verifytransaction(transid): | |
| print "Enough funds are available. Submitting transaction " + transid + "." | |
| sendtrans(transid) | |
| else: | |
| print "Error: not enough funds available for transaction " + transid + ". Deleting transaction." | |
| deletetrans(transid) | |
| def deletetrans(transid): | |
| trans=d['transaction'].pop(transid) | |
| buyer,seller=trans[1][:2] | |
| d['receipt']['debit'][buyer].remove(transid) | |
| d['receipt']['credit'][seller].remove(transid) | |
| #---test---# | |
| def init(): | |
| d=PersistentDict('bitbrick.json',flag='c',format='json'); | |
| d={}; # uncomment to start with blank dictionary | |
| if d=={}: | |
| d=PersistentDict('bitbrick.json',flag='c',format='json'); | |
| d['private'],d['public'],d['transaction'],d['receipt'],d['stored'] = \ | |
| {},{},{},{'credit':{},'debit':{}},{} | |
| return d | |
| d=init() | |
| item=newitem() | |
| newaddress(d,item) | |
| buyer=item[0] | |
| item=newitem() | |
| newaddress(d,item) | |
| seller=item[0] | |
| amount=100 | |
| d['stored'][buyer]=100 # give the buyer some stored funds. | |
| # eventually there will be code to make sure stored comes from a proven source | |
| # such as a rare hash or converted currency. | |
| trans=addtransaction(buyer, seller, amount) | |
| publishtrans(trans) | |
| buyer,seller=seller,buyer | |
| trans=addtransaction(buyer, seller, amount) | |
| #publishtrans(trans) | |
| d.close() | |
| def addtransid(d): | |
| # Adding them together makes exact order irrelevant | |
| # as long as everyone has the same set of transactions. | |
| t=0 | |
| for value in d['transaction'].values(): | |
| t += int(value[0][0]) | |
| num = t % pow(16, 16) | |
| print "Checksum of all transaction signatures is: " + str(hex(num)[2:18]) | |
| return num | |
| def genhash(d,transsum,nonce): | |
| text = str(transsum + nonce) | |
| hash = SHA256.new(text).hexdigest()[:16] | |
| return hash | |
| def checknonce(d,hash,nonce): | |
| transsum=addtransid(d) | |
| text = str(transsum + nonce) | |
| return hash == genhash(transsum,nonce) | |
| def checktranssum(d,transsum): | |
| addtransid(d) == transsum | |
| def checkwork(hash,number): | |
| return int(hash,16) <= number | |
| def trynonce(d,difficulty): | |
| nonce=int(random()*1000) # random number that gets added to transsum | |
| transsum=addtransid(d) | |
| hash = genhash(d,transsum,nonce) | |
| number=pow(16, 16)/difficulty | |
| print "This time around, " + \ | |
| "chance: 1/" + str(difficulty) | |
| print "Result: " + str(checkwork(hash,number)) | |
| print "Hash was: " + hash | |
| trynonce(d,4);trynonce(d,4);trynonce(d,4);trynonce(d,4) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment