Added initial files

This commit is contained in:
Vitalik Buterin 2013-12-19 13:38:09 -05:00
commit 6654544ca7
5 changed files with 484 additions and 0 deletions

185
blocks.py Normal file
View File

@ -0,0 +1,185 @@
from pybitcointools import *
import rlp
import re
from transactions import Transaction
class Block():
def __init__(self,data=None):
if not data:
return
if re.match('^[0-9a-fA-F]*$',data):
data = data.decode('hex')
header, tree_node_list, transaction_list, sibling_list = rlp.decode(data)
h = rlp.decode(header)
self.prevhash = encode(h[0],16,64)
self.coinbase = encode(h[1],16,40)
self.balance_root = encode(h[2],256,32)
self.contract_root = encode(h[3],256,32)
self.difficulty = h[4]
self.timestamp = h[5]
transactions_root = encode(h[6],256,32)
siblings_root = encode(h[7],256,32)
self.nonce = h[8]
self.datastore = {}
for nd in rlp.decode(tree_node_list):
ndk = bin_sha256(nd)
self.datastore[ndk] = rlp.decode(nd)
self.transactions = [Transaction(x) for x in rlp.decode(transaction_list)]
self.siblings = [rlp.decode(x) for x in rlp.decode(sibling_list)]
# Verifications
if self.balance_root != '' and self.balance_root not in self.datastore:
raise Exception("Balance Merkle root not found!")
if self.contract_root != '' and self.contract_root not in self.datastore:
raise Exception("Contract Merkle root not found!")
if bin_sha256(transaction_list) != transactions_root:
raise Exception("Transaction list root hash does not match!")
if bin_sha256(sibling_list) != sibling_root:
raise Exception("Transaction list root hash does not match!")
for sibling in self.siblings:
if sibling[0] != self.prevhash:
raise Exception("Sibling's parent is not my parent!")
hexalpha = '0123456789abcdef'
def get_updated_state(self,node,key,value):
curnode = self.datastore.get(node,None)
# Insertion case
if value != 0 and value != '':
# Base case
if key == '':
return value
# Inserting into an empty trie
if not curnode:
newnode = [ key, value ]
k = sha256(rlp.encode(newnode))
self.datastore[k] = newnode
return k
elif len(curnode) == 2:
# Inserting into a (k,v), same key
if key == curnode[0]:
newnode = [ key, value ]
k = sha256(rlp.encode(newnode))
self.datastore[k] = newnode
return k
# Inserting into a (k,v), different key
else:
i = 0
while key[:i] == curnode[0][:i]: i += 1
k1 = self.get_updated_state(None,curnode[0][i:],curnode[1])
k2 = self.get_updated_state(None,key[i:],value)
newnode3 = [ None ] * 16
newnode3[ord(curnode[0][0])] = k1
newnode3[ord(key[0])] = k2
k3 = sha256(rlp.encode(newnode3))
self.datastore[k3] = newnode3
# No prefix sharing
if i == 1:
return k3
# Prefix sharing
else:
newnode4 = [ key[:i-1], k3 ]
k4 = sha256(rlp.encode(newnode4))
self.datastore[k4] = newnode4
return k4
else:
# inserting into a 16-array
newnode1 = self.get_updated_state(curnode[ord(key[0])],key[1:],value)
newnode2 = [ curnode[i] for i in range(16) ]
newnode2[ord(key[0])] = newnode1
return newnode2
# Deletion case
else:
# Base case
if key == '':
return None
# Deleting from a (k,v); obvious
if len(curnode) == 2:
if key == curnode[0]: return None
else: return node
else:
k1 = self.get_updated_state(curnode[ord(key[0])],key[1:],value)
newnode = [ curnode[i] for i in range(16) ]
newnode[ord(key[0])] = k1
totalnodes = sum([ 1 if newnode2[i] else 0 for i in range(16) ])
if totalnodes == 0:
raise Exception("Can't delete from two to zero! Error! Waahmbulance!")
elif totalnodes == 1:
# If only have one node left, we revert to (key, value)
node_index = [i for i in range(16) if newnode2[i]][0]
node2 = self.datastore[curnode[node_index]]
if len(node2) == 2:
# If it's a (key, value), we just prepend to the key
newnode = [ chr(node_index) + node2[0], node2[1] ]
else:
# Otherwise, we just make a single-char (key, value) pair
newnode = [ chr(node_index), curnode[node_index] ]
k2 = sha256(rlp.encode(newnode))
self.datastore[k2] = newnode
return k2
def update_balance(self,address,value):
# Use out internal representation for the key
key = ''.join([chr(hexalpha.find(x)) for x in address.encode('hex')])
self.balance_root = self.get_updated_state(self.balance_root,key,value)
def update_contract_state(self,address,index,value):
# Use out internal representation for the key
key = ''.join([chr(hexalpha.find(x)) for x in (address+index).encode('hex')])
self.contract_root = self.get_updated_state(self.contract_root,key,value)
def get_state_value(self,node,key):
if key == '':
return node
if not curnode:
return None
curnode = self.datastore.get(node,None)
return self.get_state_value(curnode[ord(key[0])],key[1:])
def get_balance(self,address):
# Use out internal representation for the key
key = ''.join([chr(hexalpha.find(x)) for x in (address).encode('hex')])
return self.get_state_value(self.balance_root,key)
def get_contract_state(self,address,index):
# Use out internal representation for the key
key = ''.join([chr(hexalpha.find(x)) for x in (address+index).encode('hex')])
return self.get_state_value(self.contract_root,key)
def get_state_size(self,node):
if node is None: return 0
curnode = self.datastore.get(node,None)
if not curnode: return 0
elif len(curnode) == 2:
return self.get_state_size(curnode[1])
else:
total = 0
for i in range(16): total += self.get_state_size(curnode[i])
return total
def get_contract_size(self,address):
# Use out internal representation for the key
key = ''.join([chr(hexalpha.find(x)) for x in (address).encode('hex')])
return self.get_state_size(self.get_state_value(self.contract_root,key))
def serialize(self):
nodes = {}
def process(node):
if node is None: return
curnode = self.datastore.get(node,None)
if curnode:
index = sha256(rlp.encode(curnode))
nodes[index] = curnode
if len(node) == 2:
process(curnode[1])
elif len(node) == 16:
for i in range(16): process(curnode[i])
process(self.balance_root)
process(self.contract_root)
tree_nodes = [nodes[x] for x in nodes]
nodelist = rlp.encode(tree_nodes)
txlist = rlp.encode([x.serialize() for x in self.transactions])
siblinglist = rlp.encode(self.siblings)
header = rlp.encode([self.prevhash, self.coinbase, self.balance_root, self.contract_root, self.difficulty, self.timestamp, bin_sha256(txlist), bin_sha256(siblinglist])
return rlp.encode([header, nodelist, txlist, siblinglist])

5
parser.py Normal file
View File

@ -0,0 +1,5 @@
import rlp
def parse(inp):
if inp[0] == '\x00':
return { "type": "transaction", "data": rlp.parse(

214
processblock.py Normal file
View File

@ -0,0 +1,214 @@
from transactions import Transaction
from blocks import Block
scriptcode_map = {
0x00: 'STOP',
0x10: 'ADD',
0x11: 'SUB',
0x12: 'MUL',
0x13: 'DIV',
0x14: 'SDIV',
0x15: 'MOD',
0x16: 'SMOD',
0x17: 'EXP',
0x20: 'LT',
0x21: 'LE',
0x22: 'GT',
0x23: 'GE',
0x24: 'EQ',
0x25: 'NEG',
0x26: 'NOT',
0x30: 'SHA256',
0x31: 'RIPEMD-160',
0x32: 'ECMUL',
0x33: 'ECADD',
0x34: 'SIGN',
0x35: 'RECOVER',
0x40: 'COPY',
0x41: 'STORE',
0x42: 'LD',
0x43: 'SET',
0x50: 'JMP',
0x51: 'JMPI',
0x52: 'IND',
0x60: 'EXTRO',
0x61: 'BALANCE',
0x70: 'MKTX',
0x71: 'RAWTX',
0x80: 'DATA',
0x81: 'DATAN',
0x90: 'MYADDRESS'
}
fees = {
'stepfee': 2**60 * 8192,
'txfee': 2**60 * 524288,
'memoryfee': 2**60 * 262144,
'memory_adjust_fee': 2**60 * 65536
}
def eval_contract(block,tx):
address = tx.to
# Initialize registers
reg = [0] * 256
reg[0] = decode(tx.from,16)
reg[1] = decode(tx.to,16)
reg[2] = tx.value
reg[3] = tx.fee
index = 0
stepcounter = 0
def monop(code,f):
reg[code[2]] = f(reg[code[1]])
def binop(code,f):
reg[code[3]] = f(reg[code[1]],reg[code[2]])
while 1:
# Calculate fee
totalfee = 0
stepcounter += 1
if stepcounter > 16:
totalfee += fees.get("stepfee")
val_at_index = decode(block.get_contract_state(address,encode(index,256,32)),256)
code = [ int(val_at_index / 256**i) % 256 for i in range(6) ]
c = scriptcode_map[code[0]]
if c == 'STORE':
existing = block.get_contract_state(address,code[2])
if reg[code[1]] != 0: fee += fees["MEMORYFEE"]
if existing: fee -= fees["MEMORYFEE"]
contractbalance = block.get_balance(address)
# If we can't pay the fee...
if fee > contractbalance:
return state
# Otherwise, pay it
block.set_balance(address,contractbalance - fee)
if c == 'STOP':
break
elif c == 'ADD':
reg[code[3]] = (reg[code[1]] + reg[code[2]]) % 2**256
elif c == 'MUL':
reg[code[3]] = (reg[code[1]] * reg[code[2]]) % 2**256
elif c == 'SUB':
reg[code[3]] = (reg[code[1]] + 2**256 - reg[code[2]]) % 2**256
elif c == 'DIV':
reg[code[3]] = int(reg[code[1]] / reg[code[2]])
elif c == 'SDIV':
sign = 1
sign *= (1 if reg[code[1]] < 2**255 else -1)
sign *= (1 if reg[code[2]] < 2**255 else -1)
x = reg[code[1]] if reg[code[1]] < 2**255 else 2**256 - reg[code[1]]
y = reg[code[2]] if reg[code[2]] < 2**255 else 2**256 - reg[code[2]]
z = int(x/y)
reg[code[3]] = z if sign = 1 else 2**256 - z
elif code == 'MOD':
reg[code[3]] = reg[code[1]] % reg[code[2]]
elif code == 'SMOD':
sign = 1
sign *= (1 if reg[code[1]] < 2**255 else -1)
sign *= (1 if reg[code[2]] < 2**255 else -1)
x = reg[code[1]] if reg[code[1]] < 2**255 else 2**256 - reg[code[1]]
y = reg[code[2]] if reg[code[2]] < 2**255 else 2**256 - reg[code[2]]
z = x%y
reg[code[3]] = z if sign = 1 else 2**256 - z
elif code == 'EXP':
reg[code[3]] = pow(reg[code[1]],reg[code[2]],2**256)
elif code == 'NEG':
reg[code[2]] = 2**256 - reg[code[1]]
elif code == 'LT':
reg[code[3]] = 1 if reg[code[1]] < reg[code[2]] else 0
elif code == 'LE':
reg[code[3]] = 1 if reg[code[1]] <= reg[code[2]] else 0
elif code == 'GT':
reg[code[3]] = 1 if reg[code[1]] > reg[code[2]] else 0
elif code == 'GE':
reg[code[3]] = 1 if reg[code[1]] >= reg[code[2]] else 0
elif code == 'EQ':
reg[code[3]] = 1 if reg[code[1]] == reg[code[2]] else 0
elif code == 'NOT':
reg[code[2]] = 1 if reg[code[1]] == 0 else 0
elif code == 'SHA256':
inp = encode(reg[code[1]],256,32)
reg[code[2]] = decode(hashlib.sha256(inp).digest(),256)
elif code == 'RIPEMD-160':
inp = encode(reg[code[1]],256,32)
reg[code[2]] = decode(hashlib.new('ripemd160',inp).digest(),256)
elif code == 'ECMUL':
pt = (reg[code[1]],reg[code[2]])
# Point at infinity
if pt[0] == 0 and pt[1] == 0:
reg[code[4]], reg[code[5]] = 0,0
# Point not on curve, coerce to infinity
elif (pt[0] ** 3 + 7 - pt[1] ** 2) % N != 0:
reg[code[4]], reg[code[5]] = 0,0
# Legitimate point
else:
pt2 = base10_multiply(pt,reg[code[3]])
reg[code[4]], reg[code[5]] = pt2[0], pt2[1]
elif code == 'ECADD':
pt1 = (reg[code[1]],reg[code[2]])
pt2 = (reg[code[3]],reg[code[4]])
if (pt1[0] ** 3 + 7 - pt1[1] ** 2) % N != 0:
reg[code[5]], reg[code[6]] = 0,0
elif (pt2[0] ** 3 + 7 - pt2[1] ** 2) % N != 0:
reg[code[5]], reg[code[6]] = 0,0
else:
pt3 = base10_add(pt1,pt2)
reg[code[5]], reg[code[6]] = pt3[0], pt3[1]
elif code == 'SIGN':
reg[code[3]], reg[code[4]], reg[code[5]] = ecdsa_raw_sign(reg[code[1]],reg[code[2]])
elif code == 'RECOVER':
pt = ecdsa_raw_recover((reg[code[2]],reg[code[3]],reg[code[4]]),reg[code[1]])
reg[code[5]] = pt[0]
reg[code[6]] = pt[1]
elif code == 'COPY':
reg[code[2]] = reg[code[1]]
elif code == 'STORE':
block.update_contract_state(address,encode(reg[code[2]],256,32),reg[code[1]])
elif code == 'LD':
reg[code[2]] = block.get_contract_state(address,encode(reg[code[1]],256,32))
elif code == 'SET':
reg[code[1]] = (code[2] + 256 * code[3] + 65536 * code[4] + 16777216 * code[5]) * 2**code[6] % 2**256
elif code == 'JMP':
index = code[1]
elif code == 'JMPI':
if reg[code[1]]: index = code[2]
elif code == 'JMPX':
index = reg[code[1]]
elif code == 'JMPIX':
if reg[code[1]]: index = reg[code[2]]
elif code == 'IND':
reg[code[1]] = index
elif code == 'EXTRO':
reg[code[3]] = ethdb.get(ethdb.get(state.contract,enc160(reg[code[1]])),enc256(reg[code[2]]))
elif code == 'BALANCE':
reg[code[2]] = ethdb.get(state.balance,enc160(reg[code[1]]))
elif code == 'MKTX':
ntx = {
"ins": [ tx["receiver"] ],
"oindex": 0,
"sender": tx["receiver"],
"receiver": reg[code[1]],
"value": reg[code[2]],
"fee": reg[code[3]],
"data": []
}
for i in range(reg[code[4]]):
ntx["data"].append(ethdb.get(contract,(reg[code[5]]+i) % 2**256))
fee += ntx["fee"]
if fee > contractbalance:
return state
state.txs.append(tx)
elif code == 'DATA':
reg[code[2]] = tx["data"][code[1]]
elif code == 'DATAX':
reg[code[2]] = tx["data"][ethdb.get(contract,enc256(code[1]))]
elif code == 'DATAN':
reg[code[2]] = len(tx["data"])
elif code == 'MYADDRESS':
reg[code[1]] = tx["receiver"]
elif code == 'SUICIDE':
sz = ethdb.size(contract)
fee -= sz * state.fees["MEMORYFEE"]
contract = None
state.balance = ethdb.set(state.balance,tx["receiver"],contractbalance - fee)
state.contract = contract
return state

50
rlp.py Normal file
View File

@ -0,0 +1,50 @@
def binary_length(n):
if n == 0: return 0
else: return 1 + binary_length(n / 256)
def to_binary_array(n,L=None):
if L is None: L = binary_length(n)
if n == 0: return []
else:
x = to_binary_array(n / 256)
x.append(n % 256)
return x
def to_binary(n,L=None): return ''.join([chr(x) for x in to_binary_array(n,L)])
def from_binary(b):
if len(b) == 0: return 0
else: return ord(from_binary(b[:-1])) * 256 + b[-1]
def num_to_var_int(n):
if n < 253: s = chr(n)
else if n < 2**16: s = [253] + list(reversed(to_binary_array(n,2)))
else if n < 2**32: s = [254] + list(reversed(to_binary_array(n,4)))
else if n < 2**64: s = [255] + list(reversed(to_binary_array(n,8)))
else raise Exception("number too big")
return ''.join([chr(x) for x in s])
def decode(s):
o = []
index = 0
def read_var_int():
si = ord(s[index])
index += 1
if si < 253: return s[index - 1]
elif si == 253: read = 2
elif si == 254: read = 4
elif si == 255: read = 8
index += read
return from_binary(s[index-read:index])
while index < len(s):
L = read_var_int()
o.append(s[index:index+L])
return o
def encode(s):
if isinstance(s,(int,long)): return encode(to_binary(s))
if isinstance(s,str): return num_to_var_int(len(s))+s
else:
x = ''.join([encode(x) for x in s])
return num_to_var_int(len(s))+s

30
transactions.py Normal file
View File

@ -0,0 +1,30 @@
from pybitcointools import *
import rlp
import re
class Transaction():
def __init__(*args):
def lpad(inp,L): return '\x00' * max(0,L - len(inp)) + inp
def parse(self,data):
if re.match('^[0-9a-fA-F]*$',data):
data = data.decode('hex')
o = rlp.unparse(data)
self.to = lpad(o[0],20)
self.value = decode(o[1],256)
self.fee = decode(o[2],256)
self.data = rlp.unparse(o[-3])
self.sig = o[-4]
rawhash = sha256(rlp.encode([self.to,self.value,self.fee,self.data]))
v,r,s = ord(self.sig[0]), decode(self.sig[1:33],256), decode(self.sig[33:],256)
self.from = hash160(ecdsa_raw_recover(rawhash,(v,r,s)))
def sign(self,key):
rawhash = sha256(rlp.parse([self.to,self.value,self.fee,self.data]))
v,r,s = ecdsa_raw_sign(rawhash,args[5])
self.sig = chr(v)+encode(r,256,32)+encode(s,256,32)
self.from = hash160(privtopub(args[5]))
def serialize(self):
return rlp.parse([self.to, self.value, self.fee, self.data, self.sig]).encode('hex')
def hash(self):
return bin_sha256(self.serialize())