go-ethereum/processblock.py

343 lines
13 KiB
Python
Raw Normal View History

2013-12-19 12:38:09 -06:00
from transactions import Transaction
from blocks import Block
import time
import sys
import rlp
2013-12-19 12:38:09 -06:00
scriptcode_map = {
0x00: 'STOP',
0x10: 'ADD',
0x11: 'SUB',
0x12: 'MUL',
0x13: 'DIV',
0x14: 'SDIV',
0x15: 'MOD',
0x16: 'SMOD',
0x17: 'EXP',
2013-12-21 20:20:30 -06:00
0x18: 'NEG',
2013-12-19 12:38:09 -06:00
0x20: 'LT',
0x21: 'LE',
0x22: 'GT',
0x23: 'GE',
0x24: 'EQ',
2013-12-21 20:20:30 -06:00
0x25: 'NOT',
2013-12-19 12:38:09 -06:00
0x30: 'SHA256',
0x31: 'RIPEMD-160',
0x32: 'ECMUL',
0x33: 'ECADD',
0x34: 'ECSIGN',
0x35: 'ECRECOVER',
2013-12-19 12:38:09 -06:00
0x40: 'COPY',
0x41: 'STORE',
0x42: 'LOAD',
2013-12-19 12:38:09 -06:00
0x43: 'SET',
0x50: 'JMP',
0x51: 'JMPI',
0x52: 'IND',
0x60: 'EXTRO',
0x61: 'BALANCE',
0x70: 'MKTX',
0x71: 'RAWTX',
0x80: 'DATA',
0x81: 'DATAN',
0x90: 'MYADDRESS',
0x91: 'BLKHASH',
0xff: 'SUICIDE'
2013-12-19 12:38:09 -06:00
}
params = {
2013-12-25 07:26:43 -06:00
'stepfee': 2**64 / 64,
'txfee': 2**64,
'newcontractfee': 2**64,
'memoryfee': 2**64 / 4,
'datafee': 2**64 / 16,
'cryptofee': 2**64 / 16,
'extrofee': 2**64 / 16,
'blocktime': 60,
'period_1_reward': 2**80 * 1024,
'period_1_duration': 57600,
'period_2_reward': 2**80 * 512,
'period_2_duration': 57600,
'period_3_reward': 2**80 * 256,
'period_3_duration': 57600,
'period_4_reward': 2**80 * 128
2013-12-19 12:38:09 -06:00
}
def process_transactions(block,transactions):
while len(transactions) > 0:
tx = transactions.pop(0)
enc = (tx.value, tx.fee, tx.sender.encode('hex'), tx.to.encode('hex'))
sys.stderr.write("Attempting to send %d plus fee %d from %s to %s\n" % enc)
# Grab data about sender, recipient and miner
sdata = rlp.decode(block.state.get(tx.sender)) or [0,0,0]
tdata = rlp.decode(block.state.get(tx.to)) or [0,0,0]
# Calculate fee
if tx.to == '\x00'*20:
fee = params['newcontractfee'] + len(tx.data) * params['memoryfee']
2013-12-25 07:26:43 -06:00
else:
fee = params['txfee']
# Insufficient fee, do nothing
if fee > tx.fee:
sys.stderr.write("Insufficient fee\n")
continue
# Too much data, do nothing
if len(tx.data) > 256:
sys.stderr.write("Too many data items\n")
continue
if not sdata or sdata[1] < tx.value + tx.fee:
sys.stderr.write("Insufficient funds to send fee\n")
continue
elif tx.nonce != sdata[2] and sdata[0] == 0:
sys.stderr.write("Bad nonce\n")
continue
# Try to send the tx
if sdata[0] == 0: sdata[2] += 1
sdata[1] -= (tx.value + tx.fee)
block.reward += tx.fee
if tx.to != '':
tdata[1] += tx.value
else:
addr = tx.hash()[-20:]
adata = rlp.decode(block.state.get(addr))
if adata[2] != '':
sys.stderr.write("Contract already exists\n")
continue
block.state.update(addr,rlp.encode([1,tx.value,'']))
contract = block.get_contract(addr)
for i in range(len(tx.data)):
contract.update(encode(i,256,32),tx.data[i])
block.update_contract(addr)
print sdata, tdata
block.state.update(tx.sender,rlp.encode(sdata))
block.state.update(tx.to,rlp.encode(tdata))
# Evaluate contract if applicable
if tdata[0] == 1:
eval_contract(block,transactions,tx)
sys.stderr.write("tx processed\n")
def eval(block,transactions,timestamp,coinbase):
h = block.hash()
# Process all transactions
process_transactions(block,transactions)
# Pay miner fee
miner_state = rlp.decode(block.state.get(block.coinbase)) or [0,0,0]
block.number += 1
reward = 0
if block.number < params['period_1_duration']:
reward = params['period_1_reward']
elif block.number < params['period_2_duration']:
reward = params['period_2_reward']
elif block.number < params['period_3_duration']:
reward = params['period_3_reward']
2013-12-19 20:28:47 -06:00
else:
reward = params['period_4_reward']
print reward
miner_state[1] += reward + block.reward
2013-12-25 07:26:43 -06:00
for uncle in block.uncles:
sib_miner_state = rlp_decode(block.state.get(uncle[3]))
sib_miner_state[1] = encode(decode(sib_miner_state[1],256)+reward*7/8,256)
block.state.update(uncle[3],sib_miner_state)
miner_state[1] += reward/8
block.state.update(block.coinbase,rlp.encode(miner_state))
# Check timestamp
if timestamp < block.timestamp or timestamp > int(time.time()) + 3600:
raise Exception("timestamp not in valid range!")
# Update difficulty
if timestamp >= block.timestamp + 42:
block.difficulty += int(block.difficulty / 1024)
else:
block.difficulty -= int(block.difficulty / 1024)
block.prevhash = h
block.coinbase = coinbase
block.transactions = []
2013-12-25 07:26:43 -06:00
block.uncles = []
return block
2013-12-19 20:26:52 -06:00
def eval_contract(block,transaction_list,tx):
sys.stderr.write("evaluating contract\n")
2013-12-19 12:38:09 -06:00
address = tx.to
# Initialize registers
reg = [0] * 256
reg[0] = decode(tx.sender,256)
reg[1] = decode(tx.to,256)
2013-12-19 12:38:09 -06:00
reg[2] = tx.value
reg[3] = tx.fee
index = 0
stepcounter = 0
contract = block.get_contract(address)
if not contract:
return
2013-12-19 12:38:09 -06:00
while 1:
# Convert the data item into a code piece
val_at_index = decode(contract.get(encode(index,256,32)),256)
code = [ int(val_at_index / (256**i)) % 256 for i in range(6) ]
code[0] = scriptcode_map.get(code[0],'INVALID')
sys.stderr.write("Evaluating: "+ str(code)+"\n")
# Invalid code instruction or STOP code stops execution sans fee
if val_at_index >= 256**6 or code[0] in ['STOP','INVALID']:
sys.stderr.write("stop code, exiting\n")
break
2013-12-19 12:38:09 -06:00
# Calculate fee
minerfee = 0
nullfee = 0
2013-12-19 12:38:09 -06:00
stepcounter += 1
if stepcounter > 16:
minerfee += params["stepfee"]
2013-12-19 12:38:09 -06:00
c = scriptcode_map[code[0]]
if c in ['STORE','LOAD']:
minerfee += params["datafee"]
if c in ['EXTRO','BALANCE']:
minerfee += params["extrofee"]
if c in ['SHA256','RIPEMD-160','ECMUL','ECADD','ECSIGN','ECRECOVER']:
minerfee += params["cryptofee"]
2013-12-19 12:38:09 -06:00
if c == 'STORE':
existing = block.get_contract_state(address,code[2])
if reg[code[1]] != 0: nullfee += params["memoryfee"]
if existing: nullfee -= params["memoryfee"]
2013-12-19 12:38:09 -06:00
# If we can't pay the fee, break, otherwise pay it
if block.get_balance(address) < minerfee + nullfee:
sys.stderr.write("insufficient fee, exiting\n")
2013-12-19 12:38:09 -06:00
break
block.set_balance(address,block.get_balance(address) - nullfee - minerfee)
block.reward += minerfee
sys.stderr.write("evaluating operation\n")
# Evaluate operations
if c == 'ADD':
2013-12-19 12:38:09 -06:00
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
2013-12-19 12:38:09 -06:00
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
2013-12-19 12:38:09 -06:00
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]])
# Invalid point 1
2013-12-19 12:38:09 -06:00
if (pt1[0] ** 3 + 7 - pt1[1] ** 2) % N != 0:
reg[code[5]], reg[code[6]] = 0,0
# Invalid point 2
2013-12-19 12:38:09 -06:00
elif (pt2[0] ** 3 + 7 - pt2[1] ** 2) % N != 0:
reg[code[5]], reg[code[6]] = 0,0
# Legitimate points
2013-12-19 12:38:09 -06:00
else:
pt3 = base10_add(pt1,pt2)
reg[code[5]], reg[code[6]] = pt3[0], pt3[1]
elif code == 'ECSIGN':
2013-12-19 12:38:09 -06:00
reg[code[3]], reg[code[4]], reg[code[5]] = ecdsa_raw_sign(reg[code[1]],reg[code[2]])
elif code == 'ECRECOVER':
2013-12-19 12:38:09 -06:00
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':
contract.update(encode(reg[code[2]],256,32),reg[code[1]])
elif code == 'LOAD':
reg[code[2]] = contract.get(encode(reg[code[1]],256,32))
2013-12-19 12:38:09 -06:00
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 = reg[code[1]]
2013-12-19 20:26:52 -06:00
elif code == 'JMPI':
2013-12-19 12:38:09 -06:00
if reg[code[1]]: index = reg[code[2]]
elif code == 'IND':
reg[code[1]] = index
elif code == 'EXTRO':
if reg[code[1]] >= 2**160:
reg[code[3]] = 0
else:
address = encode(reg[code[1]],256,20)
field = encode(reg[code[2]])
reg[code[3]] = block.get_contract(address).get(field)
2013-12-19 12:38:09 -06:00
elif code == 'BALANCE':
if reg[code[1]] >= 2**160:
reg[code[2]] = 0
else:
address = encode(reg[code[1]],256,20)
reg[code[2]] = block.get_balance(address)
2013-12-19 12:38:09 -06:00
elif code == 'MKTX':
2013-12-19 20:26:52 -06:00
to = encode(reg[code[1]],256,32)
value = reg[code[2]]
fee = reg[code[3]]
if (value + fee) > block.get_balance(address):
pass
else:
datan = reg[code[4]]
data = []
for i in range(datan):
ind = encode((reg[code[5]] + i) % 2**256,256,32)
data.append(contract.get(ind))
tx = Transaction(0,to,value,fee,data)
tx.sender = address
transaction_list.insert(0,tx)
2013-12-19 12:38:09 -06:00
elif code == 'DATA':
2013-12-19 20:26:52 -06:00
reg[code[2]] = tx.data[reg[code[1]]]
2013-12-19 12:38:09 -06:00
elif code == 'DATAN':
2013-12-19 20:26:52 -06:00
reg[code[1]] = len(tx.data)
2013-12-19 12:38:09 -06:00
elif code == 'MYADDRESS':
2013-12-19 20:26:52 -06:00
reg[code[1]] = address
elif code == 'BLKHASH':
reg[code[1]] = decode(block.hash())
2013-12-19 12:38:09 -06:00
elif code == 'SUICIDE':
sz = contract.get_size()
negfee = -sz * params["memoryfee"]
toaddress = encode(reg[code[1]],256,20)
block.pay_fee(roaddress,negfee,False)
contract.root = ''
2013-12-19 20:26:52 -06:00
break
block.update_contract(address,contract)