Added initial files
This commit is contained in:
commit
6654544ca7
|
@ -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])
|
|
@ -0,0 +1,5 @@
|
|||
import rlp
|
||||
|
||||
def parse(inp):
|
||||
if inp[0] == '\x00':
|
||||
return { "type": "transaction", "data": rlp.parse(
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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())
|
Loading…
Reference in New Issue