Got the block updating, serializing and deserializing to work at least without contracts
This commit is contained in:
parent
a3af9af23e
commit
c489ff0cbb
95
blocks.py
95
blocks.py
|
@ -3,6 +3,7 @@ import rlp
|
||||||
import re
|
import re
|
||||||
from transactions import Transaction
|
from transactions import Transaction
|
||||||
from trie import Trie
|
from trie import Trie
|
||||||
|
import sys
|
||||||
|
|
||||||
class Block():
|
class Block():
|
||||||
def __init__(self,data=None):
|
def __init__(self,data=None):
|
||||||
|
@ -14,102 +15,68 @@ class Block():
|
||||||
data = data.decode('hex')
|
data = data.decode('hex')
|
||||||
|
|
||||||
header, transaction_list, self.uncles = rlp.decode(data)
|
header, transaction_list, self.uncles = rlp.decode(data)
|
||||||
[ number,
|
[ self.number,
|
||||||
self.prevhash,
|
self.prevhash,
|
||||||
self.uncles_root,
|
self.uncles_root,
|
||||||
self.coinbase,
|
self.coinbase,
|
||||||
state_root,
|
state_root,
|
||||||
self.transactions_root,
|
self.transactions_root,
|
||||||
diff,
|
self.difficulty,
|
||||||
timestamp,
|
self.timestamp,
|
||||||
nonce,
|
self.nonce,
|
||||||
self.extra ] = header
|
self.extra ] = header
|
||||||
self.number = decode(number,256)
|
self.transactions = [Transaction(x) for x in transaction_list]
|
||||||
self.difficulty = decode(difficulty,256)
|
|
||||||
self.timestamp = decode(timestamp,256)
|
|
||||||
self.nonce = decode(nonce,256)
|
|
||||||
self.transactions = [Transaction(x) for x in transaction_list)]
|
|
||||||
self.state = Trie('statedb',state_root)
|
self.state = Trie('statedb',state_root)
|
||||||
|
self.reward = 0
|
||||||
|
|
||||||
# Verifications
|
# Verifications
|
||||||
if self.state.root != '' and self.state.__get_state(self.state.root,[]) == '':
|
if self.state.root != '' and self.state.db.get(self.state.root) == '':
|
||||||
raise Exception("State Merkle root not found in database!")
|
raise Exception("State Merkle root not found in database!")
|
||||||
if bin_sha256(transaction_list) != transactions_root:
|
if bin_sha256(rlp.encode(transaction_list)) != self.transactions_root:
|
||||||
raise Exception("Transaction list root hash does not match!")
|
raise Exception("Transaction list root hash does not match!")
|
||||||
if bin_sha256(uncle_list) != uncle_root:
|
if bin_sha256(rlp.encode(self.uncles)) != self.uncles_root:
|
||||||
raise Exception("Uncle root hash does not match!")
|
raise Exception("Uncle root hash does not match!")
|
||||||
# TODO: check POW
|
# TODO: check POW
|
||||||
|
|
||||||
def send(self,tx):
|
|
||||||
# Subtract value and fee from sender account and increment nonce if applicable
|
|
||||||
sender_state = rlp.decode(self.state.get(tx.from))
|
|
||||||
if not sender_state:
|
|
||||||
return False
|
|
||||||
sender_value = decode(sender_state[1],256)
|
|
||||||
if value + fee > sender_value:
|
|
||||||
return False
|
|
||||||
sender_state[1] = encode(sender_value - value - fee,256)
|
|
||||||
# Nonce applies only to key-based addresses
|
|
||||||
if decode(sender_state[0],256) == 0:
|
|
||||||
if decode(sender_state[2],256) != tx.nonce:
|
|
||||||
return False
|
|
||||||
sender_state[2] = encode(tx.nonce + 1,256)
|
|
||||||
self.state.update(tx.from,sender_state)
|
|
||||||
# Add money to receiver
|
|
||||||
if tx.to > '':
|
|
||||||
receiver_state = rlp.decode(self.state.get(tx.to)) or ['', '', '']
|
|
||||||
receiver_state[1] = encode(decode(receiver_state[1],256) + value,256)
|
|
||||||
self.state.update(tx.to,receiver_state)
|
|
||||||
# Create a new contract
|
|
||||||
else:
|
|
||||||
addr = tx.hash()[-20:]
|
|
||||||
contract = block.get_contract(addr)
|
|
||||||
if contract.root != '': return False
|
|
||||||
for i in range(len(tx.data)):
|
|
||||||
contract.update(encode(i,256,32),tx.data[i])
|
|
||||||
block.update_contract(addr)
|
|
||||||
# Pay fee to miner
|
|
||||||
miner_state = rlp_decode(self.state.get(self.coinbase)) or ['','','']
|
|
||||||
miner_state[1] = encode(decode(miner_state[1],256) + fee,256)
|
|
||||||
self.state.update(self.coinbase,miner_state)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def pay_fee(self,address,fee,tominer=True):
|
def pay_fee(self,address,fee,tominer=True):
|
||||||
# Subtract fee from sender
|
# Subtract fee from sender
|
||||||
sender_state = rlp.decode(self.state.get(address))
|
sender_state = rlp.decode(self.state.get(address))
|
||||||
if not sender_state:
|
if not sender_state or sender_state[1] < fee:
|
||||||
return False
|
return False
|
||||||
sender_value = decode(sender_state[1],256)
|
sender_state[1] -= fee
|
||||||
if sender_value < fee:
|
|
||||||
return False
|
|
||||||
sender_state[1] = encode(sender_value - fee,256)
|
|
||||||
self.state.update(address,sender_state)
|
self.state.update(address,sender_state)
|
||||||
# Pay fee to miner
|
# Pay fee to miner
|
||||||
if tominer:
|
if tominer:
|
||||||
miner_state = rlp.decode(self.state.get(self.coinbase)) or ['','','']
|
miner_state = rlp.decode(self.state.get(self.coinbase)) or [0,0,0]
|
||||||
miner_state[1] = encode(decode(miner_state[1],256) + fee,256)
|
miner_state[1] += fee
|
||||||
self.state.update(self.coinbase,miner_state)
|
self.state.update(self.coinbase,miner_state)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_nonce(self,address):
|
def get_nonce(self,address):
|
||||||
state = rlp.decode(self.state.get(address))
|
state = rlp.decode(self.state.get(address))
|
||||||
if not state or decode(state[0],256) == 0: return False
|
if not state or state[0] == 0: return False
|
||||||
return decode(state[2],256)
|
return state[2]
|
||||||
|
|
||||||
def get_balance(self,address):
|
def get_balance(self,address):
|
||||||
state = rlp.decode(self.state.get(address))
|
state = rlp.decode(self.state.get(address))
|
||||||
return decode(state[1] || '',256)
|
return state[1] if state else 0
|
||||||
|
|
||||||
|
def set_balance(self,address,balance):
|
||||||
|
state = rlp.decode(self.state.get(address)) or [0,0,0]
|
||||||
|
state[1] = balance
|
||||||
|
self.state.update(address,rlp.encode(state))
|
||||||
|
|
||||||
|
|
||||||
# Making updates to the object obtained from this method will do nothing. You need
|
# Making updates to the object obtained from this method will do nothing. You need
|
||||||
# to call update_contract to finalize the changes.
|
# to call update_contract to finalize the changes.
|
||||||
def get_contract(self,address):
|
def get_contract(self,address):
|
||||||
state = rlp.decode(self.state.get(address))
|
state = rlp.decode(self.state.get(address))
|
||||||
if not state or decode(state[0],256) == 0: return False
|
if not state or state[0] == 0: return False
|
||||||
return Trie('statedb',state[2])
|
return Trie('statedb',state[2])
|
||||||
|
|
||||||
def update_contract(self,address,contract):
|
def update_contract(self,address,contract):
|
||||||
state = rlp.decode(self.state.get(address))
|
state = rlp.decode(self.state.get(address)) or [1,0,'']
|
||||||
if not state or decode(state[0],256) == 0: return False
|
if state[0] == 0: return False
|
||||||
state[2] = contract.root
|
state[2] = contract.root
|
||||||
self.state.update(address,state)
|
self.state.update(address,state)
|
||||||
|
|
||||||
|
@ -117,15 +84,15 @@ class Block():
|
||||||
# assuming no verification failures
|
# assuming no verification failures
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
txlist = [x.serialize() for x in self.transactions]
|
txlist = [x.serialize() for x in self.transactions]
|
||||||
header = [ encode(self.number,256),
|
header = [ self.number,
|
||||||
self.prevhash,
|
self.prevhash,
|
||||||
bin_sha256(rlp.encode(self.uncles)),
|
bin_sha256(rlp.encode(self.uncles)),
|
||||||
self.coinbase,
|
self.coinbase,
|
||||||
self.state.root,
|
self.state.root,
|
||||||
bin_sha256(rlp.encode(self.txlist)),
|
bin_sha256(rlp.encode(txlist)),
|
||||||
encode(self.difficulty,256),
|
self.difficulty,
|
||||||
encode(self.timestamp,256),
|
self.timestamp,
|
||||||
encode(self.nonce,256),
|
self.nonce,
|
||||||
self.extra ]
|
self.extra ]
|
||||||
return rlp.encode([header, txlist, self.uncles ])
|
return rlp.encode([header, txlist, self.uncles ])
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import rlp
|
||||||
|
import leveldb
|
||||||
|
from blocks import Block
|
||||||
|
from transactions import Transaction
|
||||||
|
import processblock
|
||||||
|
import hashlib
|
||||||
|
from pybitcointools import *
|
||||||
|
|
||||||
|
txpool = {}
|
||||||
|
|
||||||
|
genesis_header = [
|
||||||
|
0,
|
||||||
|
'',
|
||||||
|
bin_sha256(rlp.encode([])),
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
bin_sha256(rlp.encode([])),
|
||||||
|
2**36,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
''
|
||||||
|
]
|
||||||
|
|
||||||
|
genesis = [ genesis_header, [], [] ]
|
||||||
|
|
||||||
|
mainblk = Block(rlp.encode(genesis))
|
||||||
|
|
||||||
|
db = leveldb.LevelDB("objects")
|
||||||
|
|
||||||
|
def genaddr(seed):
|
||||||
|
priv = bin_sha256(seed)
|
||||||
|
addr = bin_sha256(privtopub(priv)[1:])[-20:]
|
||||||
|
return priv,addr
|
||||||
|
|
||||||
|
# For testing
|
||||||
|
k1,a1 = genaddr("123")
|
||||||
|
k2,a2 = genaddr("456")
|
||||||
|
|
||||||
|
def broadcast(obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def receive(obj):
|
||||||
|
d = rlp.decode(obj)
|
||||||
|
# Is transaction
|
||||||
|
if len(d) == 8:
|
||||||
|
tx = Transaction(obj)
|
||||||
|
if mainblk.get_balance(tx.sender) < tx.value + tx.fee: return
|
||||||
|
if mainblk.get_nonce(tx.sender) != tx.nonce: return
|
||||||
|
txpool[bin_sha256(blk)] = blk
|
||||||
|
broadcast(blk)
|
||||||
|
# Is message
|
||||||
|
elif len(d) == 2:
|
||||||
|
if d[0] == 'getobj':
|
||||||
|
try: return db.Get(d[1][0])
|
||||||
|
except:
|
||||||
|
try: return mainblk.state.db.get(d[1][0])
|
||||||
|
except: return None
|
||||||
|
elif d[0] == 'getbalance':
|
||||||
|
try: return mainblk.state.get_balance(d[1][0])
|
||||||
|
except: return None
|
||||||
|
elif d[0] == 'getcontractroot':
|
||||||
|
try: return mainblk.state.get_contract(d[1][0]).root
|
||||||
|
except: return None
|
||||||
|
elif d[0] == 'getcontractsize':
|
||||||
|
try: return mainblk.state.get_contract(d[1][0]).get_size()
|
||||||
|
except: return None
|
||||||
|
elif d[0] == 'getcontractstate':
|
||||||
|
try: return mainblk.state.get_contract(d[1][0]).get(d[1][1])
|
||||||
|
except: return None
|
||||||
|
# Is block
|
||||||
|
elif len(d) == 3:
|
||||||
|
blk = Block(obj)
|
||||||
|
p = block.prevhash
|
||||||
|
try:
|
||||||
|
parent = Block(db.Get(p))
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
uncles = block.uncles
|
||||||
|
for s in uncles:
|
||||||
|
try:
|
||||||
|
sib = db.Get(s)
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
processblock.eval(parent,blk.transactions,blk.timestamp,blk.coinbase)
|
||||||
|
if parent.state.root != blk.state.root: return
|
||||||
|
if parent.difficulty != blk.difficulty: return
|
||||||
|
if parent.number != blk.number: return
|
||||||
|
db.Put(blk.hash(),blk.serialize())
|
||||||
|
|
108
processblock.py
108
processblock.py
|
@ -1,6 +1,8 @@
|
||||||
from transactions import Transaction
|
from transactions import Transaction
|
||||||
from blocks import Block
|
from blocks import Block
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
|
import rlp
|
||||||
|
|
||||||
scriptcode_map = {
|
scriptcode_map = {
|
||||||
0x00: 'STOP',
|
0x00: 'STOP',
|
||||||
|
@ -61,27 +63,64 @@ params = {
|
||||||
'period_4_reward': 2**80 * 128
|
'period_4_reward': 2**80 * 128
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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']
|
||||||
|
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):
|
def eval(block,transactions,timestamp,coinbase):
|
||||||
h = block.hash()
|
h = block.hash()
|
||||||
# Process all transactions
|
# Process all transactions
|
||||||
while len(transactions) > 0:
|
process_transactions(block,transactions)
|
||||||
tx = transactions.pop(0)
|
|
||||||
fee = 0
|
|
||||||
if tx.to = '\x00'*20:
|
|
||||||
fee += params['newcontractfee'] + len(tx.data) * params['memoryfee']
|
|
||||||
else:
|
|
||||||
fee += params['txfee']
|
|
||||||
# Insufficient fee, do nothing
|
|
||||||
if fee > tx.fee: continue
|
|
||||||
# Too much data, do nothing
|
|
||||||
if len(data) > 256: continue
|
|
||||||
# Try to send the tx
|
|
||||||
if not block.send(tx): continue
|
|
||||||
# Evaluate contract if applicable
|
|
||||||
eval_contract(block,transactions,tx)
|
|
||||||
# Pay miner fee
|
# Pay miner fee
|
||||||
miner_state = rlp_decode(self.state.get(self.coinbase)) or ['','','']
|
miner_state = rlp.decode(block.state.get(block.coinbase)) or [0,0,0]
|
||||||
miner_balance = decode(miner_state[1],256)
|
|
||||||
block.number += 1
|
block.number += 1
|
||||||
reward = 0
|
reward = 0
|
||||||
if block.number < params['period_1_duration']:
|
if block.number < params['period_1_duration']:
|
||||||
|
@ -92,17 +131,17 @@ def eval(block,transactions,timestamp,coinbase):
|
||||||
reward = params['period_3_reward']
|
reward = params['period_3_reward']
|
||||||
else:
|
else:
|
||||||
reward = params['period_4_reward']
|
reward = params['period_4_reward']
|
||||||
miner_balance += reward
|
print reward
|
||||||
|
miner_state[1] += reward + block.reward
|
||||||
for uncle in block.uncles:
|
for uncle in block.uncles:
|
||||||
sib_miner_state = rlp_decode(self.state.get(uncle[3]))
|
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)
|
sib_miner_state[1] = encode(decode(sib_miner_state[1],256)+reward*7/8,256)
|
||||||
self.state.update(uncle[3],sib_miner_state)
|
block.state.update(uncle[3],sib_miner_state)
|
||||||
miner_balance += reward/8
|
miner_state[1] += reward/8
|
||||||
miner_state[1] = encode(miner_balance,256)
|
block.state.update(block.coinbase,rlp.encode(miner_state))
|
||||||
self.state.update(self.coinbase,miner_state)
|
|
||||||
# Check timestamp
|
# Check timestamp
|
||||||
if timestamp < block.timestamp or timestamp > int(time.time()) + 3600:
|
if timestamp < block.timestamp or timestamp > int(time.time()) + 3600:
|
||||||
raise new Exception("timestamp not in valid range!")
|
raise Exception("timestamp not in valid range!")
|
||||||
# Update difficulty
|
# Update difficulty
|
||||||
if timestamp >= block.timestamp + 42:
|
if timestamp >= block.timestamp + 42:
|
||||||
block.difficulty += int(block.difficulty / 1024)
|
block.difficulty += int(block.difficulty / 1024)
|
||||||
|
@ -115,10 +154,11 @@ def eval(block,transactions,timestamp,coinbase):
|
||||||
return block
|
return block
|
||||||
|
|
||||||
def eval_contract(block,transaction_list,tx):
|
def eval_contract(block,transaction_list,tx):
|
||||||
|
sys.stderr.write("evaluating contract\n")
|
||||||
address = tx.to
|
address = tx.to
|
||||||
# Initialize registers
|
# Initialize registers
|
||||||
reg = [0] * 256
|
reg = [0] * 256
|
||||||
reg[0] = decode(tx.from,256)
|
reg[0] = decode(tx.sender,256)
|
||||||
reg[1] = decode(tx.to,256)
|
reg[1] = decode(tx.to,256)
|
||||||
reg[2] = tx.value
|
reg[2] = tx.value
|
||||||
reg[3] = tx.fee
|
reg[3] = tx.fee
|
||||||
|
@ -131,8 +171,11 @@ def eval_contract(block,transaction_list,tx):
|
||||||
# Convert the data item into a code piece
|
# Convert the data item into a code piece
|
||||||
val_at_index = decode(contract.get(encode(index,256,32)),256)
|
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 = [ 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
|
# Invalid code instruction or STOP code stops execution sans fee
|
||||||
if val_at_index >= 256**6 or code[0] == 'STOP':
|
if val_at_index >= 256**6 or code[0] in ['STOP','INVALID']:
|
||||||
|
sys.stderr.write("stop code, exiting\n")
|
||||||
break
|
break
|
||||||
# Calculate fee
|
# Calculate fee
|
||||||
minerfee = 0
|
minerfee = 0
|
||||||
|
@ -154,10 +197,11 @@ def eval_contract(block,transaction_list,tx):
|
||||||
|
|
||||||
# If we can't pay the fee, break, otherwise pay it
|
# If we can't pay the fee, break, otherwise pay it
|
||||||
if block.get_balance(address) < minerfee + nullfee:
|
if block.get_balance(address) < minerfee + nullfee:
|
||||||
|
sys.stderr.write("insufficient fee, exiting\n")
|
||||||
break
|
break
|
||||||
block.pay_fee(address,nullfee,False)
|
block.set_balance(address,block.get_balance(address) - nullfee - minerfee)
|
||||||
block.pay_fee(address,minerfee,True)
|
block.reward += minerfee
|
||||||
|
sys.stderr.write("evaluating operation\n")
|
||||||
# Evaluate operations
|
# Evaluate operations
|
||||||
if c == 'ADD':
|
if c == 'ADD':
|
||||||
reg[code[3]] = (reg[code[1]] + reg[code[2]]) % 2**256
|
reg[code[3]] = (reg[code[1]] + reg[code[2]]) % 2**256
|
||||||
|
@ -174,7 +218,7 @@ def eval_contract(block,transaction_list,tx):
|
||||||
x = reg[code[1]] if reg[code[1]] < 2**255 else 2**256 - reg[code[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]]
|
y = reg[code[2]] if reg[code[2]] < 2**255 else 2**256 - reg[code[2]]
|
||||||
z = int(x/y)
|
z = int(x/y)
|
||||||
reg[code[3]] = z if sign = 1 else 2**256 - z
|
reg[code[3]] = z if sign == 1 else 2**256 - z
|
||||||
elif code == 'MOD':
|
elif code == 'MOD':
|
||||||
reg[code[3]] = reg[code[1]] % reg[code[2]]
|
reg[code[3]] = reg[code[1]] % reg[code[2]]
|
||||||
elif code == 'SMOD':
|
elif code == 'SMOD':
|
||||||
|
@ -184,7 +228,7 @@ def eval_contract(block,transaction_list,tx):
|
||||||
x = reg[code[1]] if reg[code[1]] < 2**255 else 2**256 - reg[code[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]]
|
y = reg[code[2]] if reg[code[2]] < 2**255 else 2**256 - reg[code[2]]
|
||||||
z = x%y
|
z = x%y
|
||||||
reg[code[3]] = z if sign = 1 else 2**256 - z
|
reg[code[3]] = z if sign == 1 else 2**256 - z
|
||||||
elif code == 'EXP':
|
elif code == 'EXP':
|
||||||
reg[code[3]] = pow(reg[code[1]],reg[code[2]],2**256)
|
reg[code[3]] = pow(reg[code[1]],reg[code[2]],2**256)
|
||||||
elif code == 'NEG':
|
elif code == 'NEG':
|
||||||
|
@ -278,7 +322,7 @@ def eval_contract(block,transaction_list,tx):
|
||||||
ind = encode((reg[code[5]] + i) % 2**256,256,32)
|
ind = encode((reg[code[5]] + i) % 2**256,256,32)
|
||||||
data.append(contract.get(ind))
|
data.append(contract.get(ind))
|
||||||
tx = Transaction(0,to,value,fee,data)
|
tx = Transaction(0,to,value,fee,data)
|
||||||
tx.from = address
|
tx.sender = address
|
||||||
transaction_list.insert(0,tx)
|
transaction_list.insert(0,tx)
|
||||||
elif code == 'DATA':
|
elif code == 'DATA':
|
||||||
reg[code[2]] = tx.data[reg[code[1]]]
|
reg[code[2]] = tx.data[reg[code[1]]]
|
||||||
|
|
12
rlp.py
12
rlp.py
|
@ -17,14 +17,14 @@ def from_binary(b):
|
||||||
else: return from_binary(b[:-1]) * 256 + ord(b[-1])
|
else: return from_binary(b[:-1]) * 256 + ord(b[-1])
|
||||||
|
|
||||||
def __decode(s,pos=0):
|
def __decode(s,pos=0):
|
||||||
if s == '':
|
if not s:
|
||||||
return (None, 0)
|
return (None, 0)
|
||||||
else:
|
else:
|
||||||
fchar = ord(s[pos])
|
fchar = ord(s[pos])
|
||||||
if fchar <= 24:
|
if fchar < 24:
|
||||||
return (ord(s[pos]), pos+1)
|
return (ord(s[pos]), pos+1)
|
||||||
elif fchar < 56:
|
elif fchar < 56:
|
||||||
b = ord(s[pos]) - 24
|
b = ord(s[pos]) - 23
|
||||||
return (from_binary(s[pos+1:pos+1+b]), pos+1+b)
|
return (from_binary(s[pos+1:pos+1+b]), pos+1+b)
|
||||||
elif fchar < 64:
|
elif fchar < 64:
|
||||||
b = ord(s[pos]) - 55
|
b = ord(s[pos]) - 55
|
||||||
|
@ -59,11 +59,13 @@ def decode(s): return __decode(s)[0]
|
||||||
|
|
||||||
def encode(s):
|
def encode(s):
|
||||||
if isinstance(s,(int,long)):
|
if isinstance(s,(int,long)):
|
||||||
if s <= 24:
|
if s < 0:
|
||||||
|
raise Exception("can't handle negative ints")
|
||||||
|
elif s >= 0 and s < 24:
|
||||||
return chr(s)
|
return chr(s)
|
||||||
elif s <= 2**256:
|
elif s <= 2**256:
|
||||||
b = to_binary(s)
|
b = to_binary(s)
|
||||||
return chr(len(b) + 24) + b
|
return chr(len(b) + 23) + b
|
||||||
else:
|
else:
|
||||||
b = to_binary(s)
|
b = to_binary(s)
|
||||||
b2 = to_binary(len(b))
|
b2 = to_binary(len(b))
|
||||||
|
|
|
@ -4,6 +4,7 @@ import re
|
||||||
|
|
||||||
class Transaction():
|
class Transaction():
|
||||||
def __init__(*args):
|
def __init__(*args):
|
||||||
|
self = args[0]
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
self.parse(args[1])
|
self.parse(args[1])
|
||||||
else:
|
else:
|
||||||
|
@ -17,24 +18,27 @@ class Transaction():
|
||||||
if re.match('^[0-9a-fA-F]*$',data):
|
if re.match('^[0-9a-fA-F]*$',data):
|
||||||
data = data.decode('hex')
|
data = data.decode('hex')
|
||||||
o = rlp.unparse(data)
|
o = rlp.unparse(data)
|
||||||
self.nonce = decode(o[0],256)
|
self.nonce = o[0]
|
||||||
self.to = o[1]
|
self.to = o[1]
|
||||||
self.value = decode(o[2],256)
|
self.value = o[2]
|
||||||
self.fee = decode(o[3],256)
|
self.fee = o[3]
|
||||||
self.data = rlp.unparse(o[4])
|
self.data = rlp.decode(o[4])
|
||||||
self.v = o[5]
|
self.v = o[5]
|
||||||
self.r = o[6]
|
self.r = o[6]
|
||||||
self.s = o[7]
|
self.s = o[7]
|
||||||
rawhash = sha256(rlp.encode([self.nonce,self.to,self.value,self.fee,self.data]))
|
rawhash = sha256(rlp.encode([self.nonce,self.to,self.value,self.fee,self.data]))
|
||||||
self.from = hash160(ecdsa_raw_recover(rawhash,(self.v,self.r,self.s)))
|
pub = encode_pubkey(ecdsa_raw_recover(rawhash,(self.v,self.r,self.s)),'bin')
|
||||||
|
self.sender = bin_sha256(pub[1:])[-20:]
|
||||||
|
return self
|
||||||
|
|
||||||
def sign(self,key):
|
def sign(self,key):
|
||||||
rawhash = sha256(rlp.parse([self.to,self.value,self.fee,self.data]))
|
rawhash = sha256(rlp.encode([self.to,self.value,self.fee,self.data]))
|
||||||
self.v,self.r,self.s = ecdsa_raw_sign(rawhash,key)
|
self.v,self.r,self.s = ecdsa_raw_sign(rawhash,key)
|
||||||
self.from = hash160(privtopub(key))
|
self.sender = bin_sha256(privtopub(key)[1:])[-20:]
|
||||||
|
return self
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return rlp.parse([self.nonce, self.to, self.value, self.fee, self.data, self.v, self.r, self.s])
|
return rlp.encode([self.nonce, self.to, self.value, self.fee, self.data, self.v, self.r, self.s])
|
||||||
|
|
||||||
def hex_serialize(self):
|
def hex_serialize(self):
|
||||||
return self.serialize().encode('hex')
|
return self.serialize().encode('hex')
|
||||||
|
|
39
trie.py
39
trie.py
|
@ -164,6 +164,45 @@ class Trie():
|
||||||
if curnode[16]: total += 1
|
if curnode[16]: total += 1
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
def __to_dict(self,node):
|
||||||
|
if not node: return {}
|
||||||
|
curnode = rlp.decode(self.db.get(node))
|
||||||
|
if not curnode:
|
||||||
|
raise Exception("node not found in database")
|
||||||
|
if len(curnode) == 2:
|
||||||
|
lkey = self.__decode_key(curnode[0])
|
||||||
|
o = {}
|
||||||
|
if lkey[-1] == 16:
|
||||||
|
o[curnode[0]] = curnode[1]
|
||||||
|
else:
|
||||||
|
d = self.__to_dict(curnode[1])
|
||||||
|
for v in d:
|
||||||
|
subkey = self.__decode_key(v)
|
||||||
|
totalkey = self.__encode_key(lkey+subkey)
|
||||||
|
o[totalkey] = d[v]
|
||||||
|
return o
|
||||||
|
elif len(curnode) == 17:
|
||||||
|
o = {}
|
||||||
|
for i in range(16):
|
||||||
|
d = self.__to_dict(curnode[i])
|
||||||
|
for v in d:
|
||||||
|
subkey = self.__decode_key(v)
|
||||||
|
totalkey = self.__encode_key([i] + subkey)
|
||||||
|
o[totalkey] = d[v]
|
||||||
|
if curnode[16]: o[chr(16)] = curnode[16]
|
||||||
|
return o
|
||||||
|
else:
|
||||||
|
raise Exception("bad curnode! "+curnode)
|
||||||
|
|
||||||
|
def to_dict(self,as_hex=False):
|
||||||
|
d = self.__to_dict(self.root)
|
||||||
|
o = {}
|
||||||
|
for v in d:
|
||||||
|
v2 = ''.join(['0123456789abcdef'[x] for x in self.__decode_key(v)[:-1]])
|
||||||
|
if not as_hex: v2 = v2.decode('hex')
|
||||||
|
o[v2] = d[v]
|
||||||
|
return o
|
||||||
|
|
||||||
def get(self,key):
|
def get(self,key):
|
||||||
key2 = ['0123456789abcdef'.find(x) for x in key.encode('hex')] + [16]
|
key2 = ['0123456789abcdef'.find(x) for x in key.encode('hex')] + [16]
|
||||||
return self.__get_state(self.root,key2)
|
return self.__get_state(self.root,key2)
|
||||||
|
|
Loading…
Reference in New Issue