Created
February 26, 2013 15:18
-
-
Save fcicq/5039206 to your computer and use it in GitHub Desktop.
backup of http://pastebin.com/WhL05jqg
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
# bitcoin.py | |
# | |
# I, the copyright holder of this work, hereby release it into the public | |
# domain. This applies worldwide. | |
# | |
# If this is not legally possible: | |
# I grant any entity the right to use this work for any purpose, without any | |
# conditions, unless such conditions are required by law. | |
from tools import * | |
from ecdsa import * | |
# Client version | |
_cversion = 62000 | |
# Main network magic value. Included in all message headers. | |
_magicvalue = '\xF9\xBE\xB4\xD9' | |
# 32 zero bytes | |
_zerohash = '\0' * 32 | |
# Input index for coinbase tx | |
_noindex = 0xffffffff | |
# 1 BTC | |
_bc = float(100000000) | |
# Total 21 million BTC | |
_totalcoins = 2100000000000000 | |
# 50 BTC per block | |
_startcoinbase = 5000000000 | |
# Coinbase value is halved each 210000 blocks (4 years) | |
_coinbasechange = 210000 | |
# Highest possible target (bits 1d00ffff) | |
_maxtarget = 0x00000000ffff0000000000000000000000000000000000000000000000000000 | |
# Target is changed every 2016 blocks | |
_targetchange = 2016 | |
# 2 weeks timespan | |
_targettimespan = _targetchange * 600 | |
# Min and max timespan for target change | |
_mintimespan = _targettimespan / 4 | |
_maxtimespan = _targettimespan * 4 | |
# Last block key in Db | |
_lastblock = '\0' | |
class _input: | |
"""Tx.inputs""" | |
prevtx = _zerohash # Hash of the referenced transaction | |
index = _noindex # Index of the specific output in the transaction | |
sig = '' # Signature script | |
sequence = _noindex # Transaction version as defined by sender | |
class _output: | |
"""Tx.outputs""" | |
value = 0 # Transaction value | |
pubkey = '' # Public key script | |
class _tx: | |
"""Bitcoin transaction""" | |
version = 1 # Transaction data format version | |
inputs = None # List of sources for coins | |
outputs = None # List of destination for coins | |
locktime = 0 # Block number or timestamp at whith this tx locked | |
o = 0 # New offset (o + length of tx message) | |
def decodetx(p, o = 0): | |
"""Decode tx message from bytecode. Return tx object""" | |
tx = _tx() | |
tx.version, o = getint(p, o, 4) | |
if tx.version != 1: raise err('tx.version', tx.version) | |
incount, o = getvarint(p, o) | |
tx.inputs = [] | |
for i in xrange(incount): | |
txinput = _input() | |
txinput.prevtx, o = getbin(p, o, 32) | |
txinput.index, o = getint(p, o, 4) | |
txinput.sig, o = getvarstr(p, o) | |
txinput.sequence, o = getint(p, o, 4) | |
if txinput.sequence != _noindex: | |
raise err('txinput.sequence', i, txinput.sequence) | |
tx.inputs.append(txinput) | |
outcount, o = getvarint(p, o) | |
tx.outputs = [] | |
for i in xrange(outcount): | |
txoutput = _output() | |
txoutput.value, o = getint(p, o, 8) | |
if txoutput.value < 1 or txoutput.value > _totalcoins: | |
raise err('txoutput.value', i, txoutput.value) | |
txoutput.pubkey, o = getvarstr(p, o) | |
tx.outputs.append(txoutput) | |
if not tx.inputs or not tx.outputs: raise err('empty tx') | |
tx.locktime, o = getint(p, o, 4) | |
if tx.locktime != 0 : raise err('tx.locktime', tx.locktime) | |
tx.o = o | |
return tx | |
def encodetx(tx): | |
"""Encode tx object. Return bytecode""" | |
b = putint(tx.version, 4) + putvarint(len(tx.inputs)) | |
for txinput in tx.inputs: | |
b += (putbin(txinput.prevtx, 32) + putint(txinput.index, 4) | |
+ putvarstr(txinput.sig) + putint(txinput.sequence, 4)) | |
b += putvarint(len(tx.outputs)) | |
for txoutput in tx.outputs: | |
b += putint(txoutput.value, 8) + putvarstr(txoutput.pubkey) | |
b += putint(tx.locktime, 4) | |
return b | |
class _block: | |
"""Bitcoin block""" | |
version = 1 # Block format version | |
prevblock = _zerohash # Hash of the previous block | |
merkleroot = _zerohash # Hash of transactions in block | |
timestamp = 0 # Unix timestamp | |
bits = '\xff\xff\x00\x1d' # Difficulty target | |
nonce = 0 # Random nonce | |
tx = None # List of tx objects | |
txb = None # List of tx in bytecode | |
o = 0 # New offset (o + length of block message) | |
def decodeblock(p, o = 0): | |
"""Decode block message from bytecode. Return block object""" | |
if len(p) < 80: raise err('len block header') | |
block = _block() | |
block.version, o = getint(p, o, 4) | |
if block.version != 1: raise ('block.version', block.version) | |
block.prevblock, o = getbin(p, o, 32) | |
block.merkleroot, o = getbin(p, o, 32) | |
block.timestamp, o = getint(p, o, 4) | |
block.bits, o = getbin(p, o, 4) | |
block.nonce, o = getint(p, o, 4) | |
if len(p) > 81: | |
block.tx = [] | |
block.txb = [] | |
count, o = getvarint(p, o) | |
for i in xrange(count): | |
tx = decodetx(p, o) | |
block.tx.append(tx) | |
block.txb.append(p[o:tx.o]) | |
o = tx.o | |
block.o = o | |
return block | |
def encodeblock(block): | |
"""Encode block object. Return bytecode""" | |
b = (putint(block.version, 4) + putbin(block.prevblock, 32) | |
+ putbin(block.merkleroot, 32) + putint(block.timestamp, 4) | |
+ putbin(block.bits, 4) + putint(block.nonce, 4)) | |
if block.tx: | |
b += putvarint(len(block.tx)) | |
for tx in block.tx: | |
b += encodetx(tx) | |
else: | |
b += putvarint(len(block.txb)) | |
for txb in block.txb: | |
b += txb | |
return b | |
class _netaddr: | |
"""Msg.addr""" | |
timestamp = 0 | |
services, ip, port = 1, '10.2.2.2', 8333 | |
class _netmsg: | |
"""Network message""" | |
cmd = '' # Message type | |
p = '' # Message bytecode without header | |
o = 0 # New offset (o + length of message) | |
# Cmd 'version' - exchanged when first connecting | |
version = _cversion | |
services = 1 | |
timestamp = 0 | |
recvservices, recvip, recvport = 1, '10.2.2.2', 8333 | |
fromservices, fromip, fromport = 1, '10.3.3.3', 8333 | |
nonce = '\0' * 8 | |
useragent = '' | |
startheight = 0 | |
# Cmd 'verask' - reply to version | |
# Cmd 'getaddr' - request for addr | |
# Cmd 'addr' - list of nodes | |
addr = None # List of netaddr objects | |
# Cmd 'inv' - list of new block or transactions | |
blocks = None # List of block hashes | |
txs = None # List of tx hashes | |
# Cmd 'getdata' - request for block or tx, same as inv | |
# Cmd 'getheaders' - request for headers | |
hashes = None # List of known block hashes | |
hashstop = _zerohash # Last block hash | |
# Cmd 'getblocks' - request for inv, same as getheaders | |
# Cmd 'tx' - see decodetx(), p contain tx bytecode | |
# Cmd 'block' - see decodeblock(), p contain block bytecode | |
# Cmd 'headers' - list of block headers | |
headers = None # List of block headers bytecode, see decodeblock() | |
def decodemsg(m, o = 0): | |
"""Decode message from bytecode. Return msg object""" | |
# For incompleted messages, return None | |
# For incorrent checksum, msg.cmd contain '!', msg.o - new offset | |
msg = _netmsg() | |
while True: | |
if o >= len(m): return | |
magic, o = getbin(m, o, 4) | |
if magic == _magicvalue: break | |
o -= 3 | |
msg.cmd, o = getbin(m, o, 12) | |
msg.cmd = msg.cmd.replace('\0', '') | |
length, o = getint(m, o, 4) | |
checksum, o = getbin(m, o, 4) | |
p, o = getbin(m, o, length) | |
msg.p = p | |
msg.o = o | |
if (len(p) != length): return | |
if dhash(p)[0:4] != checksum: | |
msg.cmd += '!' | |
return msg | |
if msg.cmd == 'version': | |
msg.version, o = getint(p, 0, 4) | |
msg.services, o = getint(p, o, 8) | |
msg.timestamp, o = getint(p, o, 8) | |
msg.recvservices, o = getint(p, o, 8) | |
msg.recvip, msg.recvport, o = getip(p, o) | |
msg.fromservices, o = getint(p, o, 8) | |
msg.fromip, msg.fromport, o = getip(p, o) | |
msg.nonce, o = getbin(p, o, 8) | |
msg.useragent, o = getvarstr(p, o) | |
msg.startheight, o = getint(p, o, 4) | |
elif msg.cmd == 'addr': | |
count, o = getvarint(p, 0) | |
msg.addr = [] | |
for i in xrange(count): | |
addr = _netaddr() | |
addr.timestamp, o = getint(p, o, 4) | |
addr.services, o = getint(p, o, 8) | |
addr.ip, addr.port, o = getip(p, o) | |
msg.addr.append(addr) | |
elif msg.cmd in ('inv', 'getdata'): | |
count, o = getvarint(p, 0) | |
msg.txs = [] | |
msg.blocks = [] | |
for i in xrange(count): | |
t, o = getint(p, o, 4) | |
h, o = getbin(p, o, 32) | |
if t == 1: | |
msg.txs.append(h) | |
elif t == 2: | |
msg.blocks.append(h) | |
elif msg.cmd in ('getblocks', 'getheaders'): | |
msg.version, o = getint(p, 0, 4) | |
count, o = getvarint(p, o) | |
msg.hashes = [] | |
for i in xrange(count): | |
h, o = getbin(p, o, 32) | |
msg.hashes.append(h) | |
msg.hashstop, o = getbin(p, o, 32) | |
elif msg.cmd == 'headers': | |
count, o = getvarint(p, 0) | |
msg.headers = [] | |
for i in xrange(count): | |
h, o = getbin(p, o, 81) | |
msg.headers.append(h[:80]) | |
return msg | |
def encodemsg(cmd, msg = ''): | |
"""Encode msg object or add header to bytecode""" | |
if type(msg) == str: | |
p = msg | |
elif msg.cmd == 'version': | |
if not msg.timestamp: msg.timestamp = itime() | |
p = (putint(msg.version, 4) + putint(msg.services, 8) | |
+ putint(msg.timestamp, 8) + putint(msg.recvservices, 8) | |
+ putip(msg.recvip, msg.recvport) + putint(msg.fromservices, 8) | |
+ putip(msg.fromip, msg.fromport) + putbin(msg.nonce, 8) | |
+ putvarstr(msg.useragent) + putint(msg.startheight, 4)) | |
elif msg.cmd == 'addr': | |
p = putvarint(len(msg.addr)) | |
for addr in msg.addr: | |
p += (putint(addr.timestamp, 4) + putint(addr.services, 8) | |
+ putip(addr.ip, addr.port)) | |
elif msg.cmd in ('inv', 'getdata'): | |
p = putvarint(len(msg.txs) + len(msg.blocks)) | |
for h in msg.txs: | |
p += putint(1, 4) + putbin(h, 32) | |
for h in msg.blocks: | |
p += putint(2, 4) + putbin(h, 32) | |
elif msg.cmd in ('getblocks', 'getheaders'): | |
p = putint(msg.version, 4) + putvarint(len(msg.hashes)) | |
for h in msg.hashes: | |
p += putbin(h, 32) | |
p += putbin(msg.hashstop, 32) | |
elif msg.cmd == 'headers': | |
p = putvarint(len(msg.headers)) | |
for header in msg.headers: | |
p += putbin(header, 81) | |
return _magicvalue + putbin(cmd, 12) + putint(len(p), 4) + dhash(p)[0:4] + p | |
Db = anydbm.open(...) | |
Db[_lastblock] = _zerohash | |
def getcoinbase(blocknum): | |
"""Return coinbase value for given blocknum""" | |
w = blocknum // _coinbasechange | |
a = _startcoinbase | |
for i in xrange(w): a //= 2 | |
return a | |
def decodebits(b): | |
"""Convert bits to target""" | |
i = leint(b[:3]) | |
p = ord(b[3]) | |
return i * 256 ** (p - 3) | |
def getdi(target): | |
"""Return difficulty for given target""" | |
return float(_maxtarget) / target | |
def updatetarget(oldtarget, timespan): | |
"""Calculate target for given timespan""" | |
if timespan < _mintimespan: timespan = _mintimespan | |
if timespan > _maxtimespan: timespan = _maxtimespan | |
target = oldtarget * timespan / _targettimespan | |
# Round value | |
p = 0 | |
while target > 0x7fffff: | |
target //= 256 | |
p += 1 | |
target *= 256 ** p | |
if target > _maxtarget: target = _maxtarget | |
return target | |
def verifysig(stpub, txb, i): | |
"""Verify sig pubkey script pair""" | |
txcopy = decodetx(txb) | |
stsig = txcopy.inputs[i].sig | |
if ord(stsig[0]) == len(stsig) - 1 and stsig[-1] == '\x01': | |
sig = stsig[1:-1] | |
elif stsig[ord(stsig[0]):ord(stsig[0]) + 2] == '\x01\x41': | |
sig = stsig[1:ord(stsig[0])] | |
pub = stsig[ord(stsig[0]) + 2:] | |
else: raise err('unknown sig format') | |
if ord(stpub[0]) == len(stpub) - 2 and stpub[-1] == '\xac': | |
pub = stpub[1:-1] | |
elif stpub[0:3] == '\x76\xa9\x14' and stpub[23:25] == '\x88\xac': | |
hashr = stpub[3:23] | |
if hashr != rhash(pub): return 'err rhash' | |
else: raise err('unknown pub format') | |
for k, cinput in enumerate(txcopy.inputs): | |
if i == k: cinput.sig = stpub | |
else: cinput.sig = '' | |
txcopyhash = dhash(encodetx(txcopy) + putint(1, 4)) | |
verifydigest(pub, txcopyhash, sig) | |
def decodepub(stpub): | |
"""Get rhash of pubkey from pubkey script""" | |
if ord(stpub[0]) == len(stpub) - 2 and stpub[-1] == '\xac': | |
pub = stpub[1:-1] | |
hashr = rhash(pub) | |
elif stpub[0:3] == '\x76\xa9\x14' and stpub[23:25] == '\x88\xac': | |
hashr = stpub[3:23] | |
else: raise err('unknown pub format') | |
return [hashr] | |
def readblocknum(h): | |
"""Get block number by block hash""" | |
if h not in Db: return | |
return leint(Db[h][80:]) | |
def readblockhash(n): | |
"""Get block hash by block number""" | |
n = intle(n, 4) | |
if n not in Db: return | |
return Db[n][:32] | |
def readheader(h): | |
"""Get block header bytecode by block hash or number""" | |
if type(h) == int: h = readblockhash(h) | |
if h is None or h not in Db: return | |
return Db[h][:80] | |
def readblocktx(n): | |
"""Get list of tx hashes by block hash or number""" | |
if type(n) == str: n = readblocknum(n) | |
if n is None: return | |
n = intle(n, 4) | |
if n not in Db: return | |
l = Db[n] | |
txs = [] | |
for i in xrange(32, len(l), 32): txs.append(l[i:i + 32]) | |
return txs | |
def readtx(h): | |
"""Get tx bytecode by tx hash""" | |
if h not in Db: return | |
return Db[h][6:] | |
def readtxblock(h): | |
"""Get (blocknum, txnum) by tx hash""" | |
if h not in Db: return | |
return leint(Db[h][:4]), leint(Db[h][4:6]) | |
def readblock(h): | |
"""Get block bytecode by block hash or number""" | |
bb = readheader(h) | |
if bb is None: return | |
txs = readblocktx(h) | |
bb += putvarint(len(txs)) | |
for txhash in txs: bb += readtx(txhash) | |
return bb | |
def readspend(prevtx, index): | |
"""Get new tx hash by prev tx hash and output index""" | |
k = prevtx + intle(index, 2) | |
if k not in Db: return | |
return Db[k] | |
def readaddrtx(baddr): | |
"""Get list of (blocknum, txnum) by rhash of pubkey""" | |
if baddr not in Db: return | |
l = Db[baddr] | |
txn = [] | |
for i in xrange(0, len(l), 6): | |
txn.append((leint(l[i:i + 4]), leint(l[i + 4:i + 6]))) | |
return txn | |
def readlasthash(): | |
"""Get hash of last block in blockchain""" | |
return Db[_lastblock] | |
def writedat(dat, lastblock): | |
"""Write dat records to Db""" | |
Db[_lastblock] = lastblock | |
for k in dat: | |
if len(k) == 20 and k in Db: Db[k] += dat[k] | |
elif dat[k] is None: del Db[k] | |
else: Db[k] = dat[k] | |
def verifyblocktx(b, bb, blockhash, num, dat = None): | |
"""Verify txs in block. Return dat records""" | |
if len(b.tx) < 1: raise err('empty block') | |
# Verify Merkle tree | |
if len(b.tx) == 1: | |
mroot = dhash(b.txb[0]) | |
txh = [mroot] | |
else: | |
mtree = [] | |
for tx in b.txb: | |
mtree.append(dhash(tx)) | |
txh = mtree | |
while len(mtree) != 1: | |
if len(mtree) % 2: mtree.append(mtree[-1]) | |
ntree = [] | |
for i in xrange(1, len(mtree), 2): | |
ntree.append(dhash(mtree[i - 1] + mtree[i])) | |
mtree = ntree | |
mroot = mtree[0] | |
if mroot != b.merkleroot: raise err('merkleroot') | |
# Records in dat will be added to Db | |
if dat is None: dat = {} | |
# Header record. block hash (32) : block header (80) block number (4) | |
dat[blockhash] = bb[:80] + intle(num, 4) | |
numrec = blockhash | |
for n, txb in enumerate(b.txb): | |
# Tx record. tx hash (32) : block number (4) tx number (2) tx bytecode | |
dat[txh[n]] = intle(num, 4) + intle(n, 2) + txb | |
numrec += txh[n] | |
# Num record. blocknumber (4) : block hash (32) list of tx hashes (32) | |
dat[intle(num, 4)] = numrec | |
fee = 0 | |
for n, tx in enumerate(b.tx): | |
txvalue = 0 | |
addrlist = [] | |
for i, txinput in enumerate(tx.inputs): | |
if not n: break | |
if txinput.prevtx == _zerohash: raise err('coinbase') | |
btx = readtx(txinput.prevtx) | |
if not btx and txinput.prevtx in dat: btx = dat[txinput.prevtx][6:] | |
if not btx: raise err('no prevtx') | |
ptx = decodetx(btx) | |
if txinput.index >= len(ptx.outputs): raise err('no index') | |
if readspend(txinput.prevtx, txinput.index): | |
raise err('double spend') | |
verifysig(ptx.outputs[txinput.index].pubkey, b.txb[n], i) | |
# Spend record. prev tx hash (32) prev tx index (2) : new tx hash | |
dat[txinput.prevtx + intle(txinput.index, 2)] = txh[n] | |
addrlist += decodepub(ptx.outputs[txinput.index].pubkey) | |
txvalue += ptx.outputs[txinput.index].value | |
for txoutput in tx.outputs: | |
addrlist += decodepub(txoutput.pubkey) | |
if n: txvalue -= txoutput.value | |
if txvalue < 0: raise err('txvalue') | |
fee += txvalue | |
for baddr in addrlist: | |
if baddr not in dat: dat[baddr] = '' | |
# Addr record. hashr (20) : list of block number (4) tx number (2) | |
dat[baddr] += intle(num, 4) + intle(n, 2) | |
coinbase = b.tx[0] | |
if (len(coinbase.inputs) != 1 or coinbase.inputs[0].prevtx != _zerohash | |
or coinbase.inputs[0].index != _noindex): raise err('no coinbase') | |
coinbasevalue = 0 | |
for txoutput in coinbase.outputs: coinbasevalue += txoutput.value | |
if coinbasevalue != getcoinbase(num) + fee: raise err('bad coinbase') | |
return dat | |
def acceptblock(bb): | |
"""Verify block and add to blockchain""" | |
blockhash = dhash(bb[:80]) | |
if readheader(blockhash): raise err('duplicate') | |
b = decodeblock(bb) | |
blocktarget = decodebits(b.bits) | |
# Hash of the block header must be lower than or equal to the target | |
if leint(blockhash) > blocktarget: raise err('blockhash') | |
lastblock = readlasthash() | |
if b.prevblock == lastblock: | |
# Add to main branch | |
pass | |
else: raise err('prevblock') | |
if lastblock != _zerohash: | |
num = readblocknum(lastblock) + 1 | |
# Last target change block | |
tbnum = (num - 1) // _targetchange * _targetchange | |
tb = decodeblock(readheader(tbnum)) | |
# Current target | |
target = decodebits(tb.bits) | |
# Update target every 2016 block | |
if not num % _targetchange: | |
pb = decodeblock(readheader(num - 1)) | |
target = updatetarget(target, pb.timestamp - tb.timestamp) | |
else: | |
num = 0 | |
target = _maxtarget | |
# Bits must be equal to the current target | |
if blocktarget != target: raise err('bits') | |
dat = verifyblocktx(b, bb, blockhash, num) | |
"""if branch not in Branches: Branches.append(branch) | |
if branch is not Mainbranch: | |
if branch.di > Mainbranch.di: | |
# A side branch becoming the main branch | |
del Branches[branchid] | |
p = b | |
# For blocks in new main branch | |
while _sidebranch + p.prevblock in Db: | |
del Db[_sidebranch + p.prevblock] | |
p = decodeblock(p.prevblock) | |
forkblock = p.prevblock | |
branchid = len(Branches) | |
phash = Mainbranch.lastblock | |
# For blocks in old main branch | |
while phash != forkblock: | |
Db[_sidebranch + phash] = branchid | |
p = decodeblock(phash) | |
phash = p.prevblock | |
Branches.append(Branches[0]) | |
Branches[0] = branch | |
else: | |
Db[_sidebranch + blockhash] = branchid""" | |
writedat(dat, blockhash) | |
def docommand(c): | |
r = '' | |
c = c.split(' ') | |
if c[0] == 'blocks': | |
if len(c) < 3: count = 20 | |
else: count = int(c[2]) | |
if len(c) < 2: start = readblocknum(readlasthash()) - 19 | |
else: start = int(c[1]) | |
for num in xrange(start, start + count): | |
bh = readblockhash(num) | |
if not bh: break | |
b = decodeblock(readheader(bh)) | |
r += str(num) + ' ' + stime(b.timestamp) + '\n' + tohexl(bh) + '\n' | |
if c[0] == 'tx': | |
if len(c[1]) == 64: | |
txhash = unhexl(c[1]) | |
blocknum, txnum = readtxblock(txhash) | |
else: | |
l = c[1].split('.') | |
blocknum = int(l[0]) | |
txnum = int(l[1]) | |
txs = readblocktx(blocknum) | |
txhash = txs[txnum] | |
tx = decodetx(readtx(txhash)) | |
r += str(blocknum) + '.' + str(txnum) + ' ' + tohexl(txhash) + '\n' | |
txvalue = 0 | |
if txnum: | |
r += 'From:\n' | |
for txinput in tx.inputs: | |
ptx = decodetx(readtx(txinput.prevtx)) | |
pb, pt = readtxblock(txinput.prevtx) | |
poutput = ptx.outputs[txinput.index] | |
r += str(poutput.value / _bc) + ' ' | |
r += encodeaddr(decodepub(poutput.pubkey)[0]) + ' ' | |
r += str(pb) + '.' + str(pt) + '.' + str(txinput.index) + '\n' | |
txvalue += poutput.value | |
r += 'To:\n' | |
for txoutput in tx.outputs: | |
r += str(txoutput.value / _bc) + ' ' | |
r += encodeaddr(decodepub(txoutput.pubkey)[0]) + '\n' | |
txvalue -= txoutput.value | |
txvalue /= _bc | |
if not txnum: r += 'Generation: ' + str(- txvalue) + '\n' | |
elif txvalue: r += 'Fee: ' + str(txvalue) + '\n' | |
if c[0] == 'block': | |
if len(c[1]) == 64: | |
bh = unhexl(c[1]) | |
num = readblocknum(bh) | |
else: | |
num = int(c[1]) | |
bh = readblockhash(num) | |
r += str(num) + ' ' + tohexl(bh) + '\n' | |
b = decodeblock(readheader(bh)) | |
r += 'Prev block: ' + tohexl(b.prevblock) + '\n' | |
nextblock = readblockhash(num + 1) | |
if nextblock: r += 'Next block: ' + tohexl(nextblock) + '\n' | |
else: r += 'Last block\n' | |
r += 'Merkle root: ' + tohexl(b.merkleroot) + '\n' | |
r += 'Time: ' + stime(b.timestamp) + ' (' + str(b.timestamp) + ')\n' | |
r += 'Difficulty: ' + str(getdi(decodebits(b.bits))) | |
r += ' (Bits: ' + tohexl(b.bits) + ')\n' | |
r += 'Nonce: ' + str(b.nonce) + '\n' | |
txs = readblocktx(bh) | |
for txhash in txs: r += '\n' + docommand('tx ' + tohexl(txhash)) | |
if c[0] == 'addr': | |
hashr = decodeaddr(c[1]) | |
txn = readaddrtx(hashr) | |
for blocknum, txnum in txn: | |
r += '\n' + docommand('tx ' + str(blocknum) + '.' + str(txnum)) | |
return r | |
def verifydigest(*a): pass | |
def ver(): | |
count = 2000 | |
count = -1 | |
timestart = time() | |
try: | |
fb = open('blockchain', 'r') | |
o = 0 | |
bc = 1024 * 1024 * 10 | |
bs = 1024 * 1024 * 12 | |
buf = fb.read(bs) | |
while True: | |
if o > bc: | |
buf = buf[o:] | |
o = 0 | |
buf += fb.read(bs) | |
if o >= len(buf): break | |
block = decodeblock(buf, o) | |
acceptblock(buf[o:block.o]) | |
print readblocknum(readlasthash()), \ | |
stime(decodeblock(readheader(readlasthash())).timestamp), \ | |
len(readblocktx(readlasthash())) | |
o = block.o | |
count -= 1 | |
if count == 0: break | |
finally: | |
timespan = time() - timestart | |
fb.close() | |
print '-' * 78 | |
print 'Last block:' | |
print readblocknum(readlasthash()), \ | |
stime(decodeblock(readheader(readlasthash())).timestamp) | |
print tohexl(readlasthash()) | |
print timespan, 'sec' | |
print (readblocknum(readlasthash()) + 1) / timespan, 'block/sec' | |
print '-' * 80 | |
from traceback import format_exc | |
while True: | |
c = raw_input() | |
try: | |
print docommand(c) | |
except: | |
print format_exc() | |
def testsignmsg(): | |
timestart = time() | |
for i in xrange(100): | |
print 'i', i | |
priv = genkey() | |
print 'priv', tohex(priv) | |
msg = repr(urandom(i * 8 + 15)) | |
print 'msg', msg | |
sig = signmsg(priv, msg) | |
print 'sig', sig | |
pub = getpubkey(priv) | |
print 'pub', tohex(pub) | |
v = rhash(pub) | |
print 'v', tohex(v) | |
a = encodeaddr(v) | |
print 'a', a | |
r = decodeaddr(a) | |
print 'r', tohex(r) | |
if r != v: raise err('r != v') | |
verifymsg(r, msg, sig) | |
digest = dhash(msg) | |
print 'digest', tohex(digest) | |
s = signdigest(priv, digest) | |
print 's', tohex(s) | |
verifydigest(pub, digest, s) | |
for n in xrange(2): | |
for m in xrange(3): | |
print 'n:m', str(n) + ':' + str(m) | |
npriv = nprivkey(priv, str(n) + ':' + str(m), pub) | |
print 'npriv', tohex(npriv) | |
d = urandom(32) | |
print 'd', tohex(d) | |
g = signdigest(npriv, d) | |
print 'g', tohex(g) | |
npub = npubkey(pub, str(n) + ':' + str(m)) | |
print 'npub', tohex(npub) | |
verifydigest(npub, d, g) | |
vpub = getpubkey(npriv) | |
print 'vpub', tohex(vpub) | |
if vpub != npub: raise err('vpub != npub') | |
print time() - timestart, 'sec' |
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
# ecdsa.py | |
# | |
# I, the copyright holder of this work, hereby release it into the public | |
# domain. This applies worldwide. | |
# | |
# If this is not legally possible: | |
# I grant any entity the right to use this work for any purpose, without any | |
# conditions, unless such conditions are required by law. | |
from tools import * | |
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL | |
_n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L | |
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L | |
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L | |
_gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L | |
_gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L | |
def randkey(): | |
while True: | |
r = beint(urandom(32)) + 1 | |
if 1 <= r < _n: return r | |
def inversemod(a, m): | |
if a < 0 or m <= a: a = a % m | |
c, d = a, m | |
uc, vc, ud, vd = 1, 0, 0, 1 | |
while c != 0: | |
q, c, d = divmod(d, c) + (c,) | |
uc, vc, ud, vd = ud - q * uc, vd - q * vc, uc, vc | |
if d != 1: raise err('d is not 1', a, m) | |
if ud > 0: return ud | |
else: return ud + m | |
def doublepoint(x, y): | |
if x is None: return None, None | |
l = ((3 * x * x + _a) * inversemod(2 * y, _p)) % _p | |
x3 = (l * l - 2 * x) % _p | |
y3 = (l * (x - x3) - y) % _p | |
return x3, y3 | |
def addpoint(x1, y1, x2, y2): | |
if x2 is None: return x1, y1 | |
if x1 is None: return x2, y2 | |
if x1 == x2: | |
if (y1 + y2) % _p == 0: | |
return None, None | |
else: | |
return doublepoint(x1, y1) | |
l = ((y2 - y1) * inversemod(x2 - x1, _p)) % _p | |
x3 = (l * l - x1 - x2) % _p | |
y3 = (l * (x1 - x3) - y1) % _p | |
return x3, y3 | |
def mulpoint(x, y, e): | |
e = e % _n | |
if e <= 0 or x is None: return None, None | |
e3 = 3 * e | |
i = 1 | |
while i <= e3: i *= 2 | |
i //= 2 | |
x3, y3 = x, y | |
i //= 2 | |
while i > 1: | |
x3, y3 = doublepoint(x3, y3) | |
if (e3 & i) != 0 and (e & i) == 0: x3, y3 = addpoint(x3, y3, x, y) | |
if (e3 & i) == 0 and (e & i) != 0: x3, y3 = addpoint(x3, y3, x, -y) | |
i //= 2 | |
return x3, y3 | |
def checkpubkey(x, y): | |
if (y * y - (x * x * x + _a * x + _b)) % _p != 0: | |
raise err('not on curve', x, y) | |
if mulpoint(x, y, _n) != (None, None): | |
raise err('mul to _n is not None', x, y) | |
return True | |
def sign(key, digest, nonce): | |
k = nonce % _n | |
r, y = mulpoint(_gx, _gy, k) | |
if r == 0: raise err('r is 0', key, digest, nonce) | |
s = (inversemod(k, _n) * (digest + (key * r) % _n)) % _n | |
if s == 0: return err('s is 0', key, digest, nonce) | |
return r, s | |
def verify(x, y, digest, r, s): | |
checkpubkey(x, y) | |
if r < 1 or r > _n - 1: raise err('incorrect r', r) | |
if s < 1 or s > _n - 1: raise err('incorrect s', s) | |
c = inversemod(s, _n) | |
u1 = (digest * c ) % _n | |
u2 = (r * c) % _n | |
x1, y1 = mulpoint(_gx, _gy, u1) | |
x2, y2 = mulpoint(x, y, u2) | |
x, y = addpoint(x1, y1, x2, y2) | |
v = x % _n | |
if v == r: return True | |
else: raise err('signature failed', x, y, digest, r, s) | |
def genkey(): | |
"""Generate random private key""" | |
return intbe(randkey(), 32) | |
def getpubkey(privkey): | |
"""Return public key for given private key""" | |
if len(privkey) != 32: raise err('len privkey') | |
x, y = mulpoint(_gx, _gy, beint(privkey)) | |
return '\x04' + intbe(x, 32) + intbe(y, 32) | |
def signdigest(privkey, digest): | |
"""Sign digest (32 bytes). Return signature in DER format""" | |
if len(privkey) != 32 or len(digest) != 32: raise err('len privkey') | |
r, s = sign(beint(privkey), beint(digest), randkey()) | |
r = intbe(r) | |
if ord(r[0]) > 0x7f: r = '\0' + r | |
sig = '\x02' + chr(len(r)) + r | |
s = intbe(s) | |
if ord(s[0]) > 0x7f: s = '\0' + s | |
sig += '\x02' + chr(len(s)) + s | |
sig = '\x30' + chr(len(sig)) + sig | |
return sig | |
def verifydigest(pubkey, digest, sig): | |
"""Check signature (DER)""" | |
if len(pubkey) != 65 or pubkey[0] != '\x04': raise err('len pubkey') | |
x = beint(pubkey[1:33]) | |
y = beint(pubkey[33:]) | |
if len(digest) != 32: raise err('len digest') | |
if sig[0] != '\x30' or len(sig) != ord(sig[1]) + 2: raise err('len sig') | |
r = beint(sig[4:4 + ord(sig[3])]) | |
s = beint(sig[6 + ord(sig[3]):]) | |
return verify(x, y, beint(digest), r, s) | |
def nprivkey(mprivkey, n, mpubkey = None): | |
"""Get private key from main private key with n sequence (n is str)""" | |
if len(mprivkey) != 32: raise err('len privkey') | |
if not mpubkey: | |
mpubkey = getpubkey(mprivkey) | |
h = beint(dhash(n + ':' + mpubkey[1:])) | |
return intbe((beint(mprivkey) + h) % _n, 32) | |
def npubkey(mpubkey, n): | |
"""Get public key from main public key with n sequence (n is str)""" | |
if len(mpubkey) != 65 or mpubkey[0] != '\x04': raise err('len pubkey') | |
x1 = beint(mpubkey[1:33]) | |
y1 = beint(mpubkey[33:]) | |
h = beint(dhash(n + ':' + mpubkey[1:])) | |
x2, y2 = mulpoint(_gx, _gy, h) | |
x, y = addpoint(x1, y1, x2, y2) | |
return '\x04' + intbe(x, 32) + intbe(y, 32) | |
def rspubkey(digest, r, s, k): | |
x = r + (k / 2) * _n | |
a1 = (x * x * x + _a * x + _b) % _p | |
b1 = pow(a1, (_p + 1) / 4, _p) | |
if (b1 - k) % 2 == 0: | |
y = b1 | |
else: | |
y = _p - b1 | |
checkpubkey(x, y) | |
x1, y1 = mulpoint(x, y, s) | |
x2, y2 = mulpoint(_gx, _gy, -digest % _n) | |
x, y = addpoint(x1, y1, x2, y2) | |
x, y = mulpoint(x, y, inversemod(r, _n)) | |
return x, y | |
def msgmagic(msg): | |
return "\x18Bitcoin Signed Message:\n" + intle(len(msg)) + msg | |
def verifymsg(hashr, msg, sig): | |
"""Verify message using riperm160 of sha256 hash of public key""" | |
if len(hashr) != 20: raise err('len hashr') | |
if len(sig) > 65: sig = b64decode(sig) | |
if len(sig) == 65: | |
ka = [ord(sig[0]) - 27] | |
r = beint(sig[1:33]) | |
s = beint(sig[33:]) | |
elif len(sig) == 64: | |
ka = xrange(2) | |
r = beint(sig[:32]) | |
s = beint(sig[32:]) | |
else: | |
raise err('len sig') | |
digest = beint(dhash(msgmagic(msg))) | |
for k in ka: | |
x, y = rspubkey(digest, r, s, k) | |
if hashr == rhash('\x04' + intbe(x, 32) + intbe(y, 32)): | |
verify(x, y, digest, r, s) | |
return chr(k + 27) | |
raise err('verify hashr failed', hashr, msg, sig) | |
def signmsg(privkey, msg): | |
"""Sign message. Return sig in Base64""" | |
if len(privkey) != 32: raise err('len privkey') | |
r, s = sign(beint(privkey), beint(dhash(msgmagic(msg))), randkey()) | |
sig = intbe(r, 32) + intbe(s, 32) | |
c = verifymsg(rhash(getpubkey(privkey)), msg, sig) | |
return b64encode(c + sig) |
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
# tools.py | |
# | |
# I, the copyright holder of this work, hereby release it into the public | |
# domain. This applies worldwide. | |
# | |
# If this is not legally possible: | |
# I grant any entity the right to use this work for any purpose, without any | |
# conditions, unless such conditions are required by law. | |
from hashlib import sha256, new as hashnew | |
from time import time, gmtime, strftime | |
from binascii import b2a_hex as tohex, a2b_hex as unhex | |
from base64 import b64encode, b64decode | |
from os import urandom | |
err = RuntimeError | |
def itime(): | |
"""Return current timestamp as int""" | |
return int(time()) | |
def stime(t): | |
"""Convert timestamp to string UTC""" | |
return strftime('%d.%m.%Y %H:%M:%S', gmtime(t)) | |
def intle(i, l = None): | |
"""Convert int to little endian bytecode""" | |
b = '' | |
while i != 0: | |
b += chr(i % 256) | |
i //= 256 | |
if l: | |
b += '\0' * (l - len(b)) | |
if len(b) != l: raise err('integer overflow', i, l) | |
return b | |
def leint(b): | |
"""Convert little endian bytecode to int""" | |
i = 0 | |
p = 1 | |
for c in b: | |
i += ord(c) * p | |
p *= 256 | |
return i | |
def intbe(i, l = None): | |
"""Convert int to big endian bytecode""" | |
b = '' | |
while i != 0: | |
b = chr(i % 256) + b | |
i //= 256 | |
if l: | |
b = '\0' * (l - len(b)) + b | |
if len(b) != l: raise err('integer overflow', i, l) | |
return b | |
def beint(b): | |
"""Convert big endian bytecode to int""" | |
i = 0 | |
for c in b: | |
i = ord(c) + i * 256 | |
return i | |
def tohexl(b): | |
"""Convert little endian bytecode to hex""" | |
return tohex(b[::-1]) | |
def unhexl(h): | |
"""Convert hex to little endian bytecode""" | |
return unhex(h)[::-1] | |
def dhash(s): | |
"""Double sha256""" | |
return sha256(sha256(s).digest()).digest() | |
def rhash(s): | |
"""Ripemd160 of sha256""" | |
r = hashnew('ripemd160') | |
r.update(sha256(s).digest()) | |
return r.digest() | |
b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | |
def encodeaddr(b, v = 0): | |
"""Encode Base64Check""" | |
b = chr(v) + b | |
b += dhash(b)[:4] | |
i = beint(b) | |
a = '' | |
while i != 0: | |
a = b58[i % 58] + a | |
i //= 58 | |
z = 0 | |
for c in b: | |
if c == '\0': z += 1 | |
else: break | |
return b58[0] * z + a | |
def decodeaddr(a, v = 0): | |
"""Decode Base64Check""" | |
i = 0 | |
for c in a: | |
i = b58.find(c) + i * 58 | |
b = intbe(i) | |
z = 0 | |
for c in a: | |
if c == b58[0]: z += 1 | |
else: break | |
b = '\0' * z + b | |
if b[0] != chr(v): raise err('version', a, v) | |
if dhash(b[:-4])[:4] != b[-4:]: raise err('checksum', a, v) | |
return b[1:-4] | |
def putint(i, l): | |
"""Convert int to bytecode integer size l""" | |
return intle(i, l) | |
def putvarint(i): | |
"""Convert int to bytecode variable length interger""" | |
if i < 0xfd: | |
l = 1 | |
f = '' | |
elif i < 0xffff: | |
l = 2 | |
f = '\xfd' | |
elif i < 0xffffffff: | |
l = 4 | |
f = '\xfe' | |
else: | |
l = 8 | |
f = '\xff' | |
return f + intle(i, l) | |
def putvarstr(s): | |
"""Convert str to bytecode variable length string""" | |
return putvarint(len(s)) + s | |
def putbin(s, l): | |
"""Convert str to bytecode fixed length string""" | |
while len(s) < l: s += chr(0) | |
if len(s) != l: raise err('string overflow', s, l) | |
return s | |
def putip(ip, port): | |
"""Convert ip and port to bytecode""" | |
b = '\0' * 10 + '\xFF\xFF' | |
for n in ip.split('.'): b += chr(int(n)) | |
return b + chr(port // 256) + chr(port % 256) | |
def getint(s, o, l): | |
"""Read integer from bytecode offset o size l. Return (int, new_offset)""" | |
return leint(s[o:o + l]), o + l | |
def getvarint(s, o): | |
"""Read variable length integer from bytecode. Return (int, new_offset)""" | |
if s[o] == '\xfd': l = 2 | |
elif s[o] == '\xfe': l = 4 | |
elif s[o] == '\xff': l = 8 | |
else: l = 1 | |
if l > 1: o += 1 | |
return leint(s[o:o + l]), o + l | |
def getvarstr(s, o): | |
"""Read variable length string from bytecode. Return (int, new_offset)""" | |
l, o = getvarint(s, o) | |
return s[o:o + l], o + l | |
def getbin(s, o, l): | |
"""Read fixed length string from bytecode. Return (str, new_offset)""" | |
return s[o:o + l], o + l | |
def getip(s, o): | |
"""Read ip and port from bytecode. Return (ip, port, new_offset)""" | |
if s[o:o + 12] == '\0' * 10 + '\xFF\xFF': | |
return (str(ord(s[o + 12])) + '.' + str(ord(s[o + 13])) + '.' | |
+ str(ord(s[o + 14])) + '.' + str(ord(s[o + 15])), | |
ord(s[o + 16]) * 256 + ord(s[o + 17]), o + 18) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment