Finished everything except the miner, PoW validator and testing
This commit is contained in:
parent
bce41be4f7
commit
0d729e1219
263
blocks.py
263
blocks.py
|
@ -2,35 +2,38 @@ from pybitcointools import *
|
||||||
import rlp
|
import rlp
|
||||||
import re
|
import re
|
||||||
from transactions import Transaction
|
from transactions import Transaction
|
||||||
|
from trie import Trie
|
||||||
|
|
||||||
class Block():
|
class Block():
|
||||||
def __init__(self,data=None):
|
def __init__(self,data=None):
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
if re.match('^[0-9a-fA-F]*$',data):
|
if re.match('^[0-9a-fA-F]*$',data):
|
||||||
data = data.decode('hex')
|
data = data.decode('hex')
|
||||||
header, tree_node_list, transaction_list, sibling_list = rlp.decode(data)
|
|
||||||
h = rlp.decode(header)
|
header, transaction_list, self.siblings = rlp.decode(data)
|
||||||
self.prevhash = encode(h[0],16,64)
|
[ number,
|
||||||
self.coinbase = encode(h[1],16,40)
|
self.prevhash,
|
||||||
self.balance_root = encode(h[2],256,32)
|
self.siblings_root,
|
||||||
self.contract_root = encode(h[3],256,32)
|
self.coinbase,
|
||||||
self.difficulty = h[4]
|
state_root,
|
||||||
self.timestamp = h[5]
|
self.transactions_root,
|
||||||
transactions_root = encode(h[6],256,32)
|
diff,
|
||||||
siblings_root = encode(h[7],256,32)
|
timestamp,
|
||||||
self.nonce = h[8]
|
nonce,
|
||||||
self.datastore = {}
|
self.extra ] = header
|
||||||
for nd in rlp.decode(tree_node_list):
|
self.number = decode(number,256)
|
||||||
ndk = bin_sha256(nd)
|
self.difficulty = decode(difficulty,256)
|
||||||
self.datastore[ndk] = rlp.decode(nd)
|
self.timestamp = decode(timestamp,256)
|
||||||
self.transactions = [Transaction(x) for x in rlp.decode(transaction_list)]
|
self.nonce = decode(nonce,256)
|
||||||
self.siblings = [rlp.decode(x) for x in rlp.decode(sibling_list)]
|
self.transactions = [Transaction(x) for x in transaction_list)]
|
||||||
|
self.state = Trie('statedb',state_root)
|
||||||
|
|
||||||
# Verifications
|
# Verifications
|
||||||
if self.balance_root != '' and self.balance_root not in self.datastore:
|
if self.state.root != '' and self.state.__get_state(self.state.root,[]) == '':
|
||||||
raise Exception("Balance Merkle root not found!")
|
raise Exception("State Merkle root not found in database!")
|
||||||
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:
|
if bin_sha256(transaction_list) != transactions_root:
|
||||||
raise Exception("Transaction list root hash does not match!")
|
raise Exception("Transaction list root hash does not match!")
|
||||||
if bin_sha256(sibling_list) != sibling_root:
|
if bin_sha256(sibling_list) != sibling_root:
|
||||||
|
@ -38,148 +41,96 @@ class Block():
|
||||||
for sibling in self.siblings:
|
for sibling in self.siblings:
|
||||||
if sibling[0] != self.prevhash:
|
if sibling[0] != self.prevhash:
|
||||||
raise Exception("Sibling's parent is not my parent!")
|
raise Exception("Sibling's parent is not my parent!")
|
||||||
|
# TODO: check POW
|
||||||
|
|
||||||
|
def send(self,tx):
|
||||||
hexalpha = '0123456789abcdef'
|
# Subtract value and fee from sender account and increment nonce if applicable
|
||||||
|
sender_state = rlp.decode(self.state.get(tx.from))
|
||||||
def get_updated_state(self,node,key,value):
|
if not sender_state:
|
||||||
curnode = self.datastore.get(node,None)
|
return False
|
||||||
# Insertion case
|
sender_value = decode(sender_state[1],256)
|
||||||
if value != 0 and value != '':
|
if value + fee > sender_value:
|
||||||
# Base case
|
return False
|
||||||
if key == '':
|
sender_state[1] = encode(sender_value - value - fee,256)
|
||||||
return value
|
# Nonce applies only to key-based addresses
|
||||||
# Inserting into an empty trie
|
if decode(sender_state[0],256) == 0:
|
||||||
if not curnode:
|
if decode(sender_state[2],256) != tx.nonce:
|
||||||
newnode = [ key, value ]
|
return False
|
||||||
k = sha256(rlp.encode(newnode))
|
sender_state[2] = encode(tx.nonce + 1,256)
|
||||||
self.datastore[k] = newnode
|
self.state.update(tx.from,sender_state)
|
||||||
return k
|
# Add money to receiver
|
||||||
elif len(curnode) == 2:
|
if tx.to > '':
|
||||||
# Inserting into a (k,v), same key
|
receiver_state = rlp.decode(self.state.get(tx.to)) or ['', '', '']
|
||||||
if key == curnode[0]:
|
receiver_state[1] = encode(decode(receiver_state[1],256) + value,256)
|
||||||
newnode = [ key, value ]
|
self.state.update(tx.to,receiver_state)
|
||||||
k = sha256(rlp.encode(newnode))
|
# Create a new contract
|
||||||
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:
|
else:
|
||||||
# Base case
|
addr = tx.hash()[:20]
|
||||||
if key == '':
|
contract = block.get_contract(addr)
|
||||||
return None
|
if contract.root != '': return False
|
||||||
# Deleting from a (k,v); obvious
|
for i in range(len(tx.data)):
|
||||||
if len(curnode) == 2:
|
contract.update(encode(i,256,32),tx.data[i])
|
||||||
if key == curnode[0]: return None
|
block.update_contract(addr)
|
||||||
else: return node
|
# Pay fee to miner
|
||||||
else:
|
miner_state = rlp_decode(self.state.get(self.coinbase)) or ['','','']
|
||||||
k1 = self.get_updated_state(curnode[ord(key[0])],key[1:],value)
|
miner_state[1] = encode(decode(miner_state[1],256) + fee,256)
|
||||||
newnode = [ curnode[i] for i in range(16) ]
|
self.state.update(self.coinbase,miner_state)
|
||||||
newnode[ord(key[0])] = k1
|
return True
|
||||||
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 pay_fee(self,address,fee,tominer=True):
|
||||||
|
# Subtract fee from sender
|
||||||
|
sender_state = rlp.decode(self.state.get(address))
|
||||||
|
if not sender_state:
|
||||||
|
return False
|
||||||
|
sender_value = decode(sender_state[1],256)
|
||||||
|
if sender_value < fee:
|
||||||
|
return False
|
||||||
|
sender_state[1] = encode(sender_value - fee,256)
|
||||||
|
self.state.update(address,sender_state)
|
||||||
|
# Pay fee to miner
|
||||||
|
if tominer:
|
||||||
|
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 update_balance(self,address,value):
|
def get_nonce(self,address):
|
||||||
# Use out internal representation for the key
|
state = rlp.decode(self.state.get(address))
|
||||||
key = ''.join([chr(hexalpha.find(x)) for x in address.encode('hex')])
|
if not state or decode(state[0],256) == 0: return False
|
||||||
self.balance_root = self.get_updated_state(self.balance_root,key,value)
|
return decode(state[2],256)
|
||||||
|
|
||||||
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):
|
def get_balance(self,address):
|
||||||
# Use out internal representation for the key
|
state = rlp.decode(self.state.get(address))
|
||||||
key = ''.join([chr(hexalpha.find(x)) for x in (address).encode('hex')])
|
return decode(state[1] || '',256)
|
||||||
return self.get_state_value(self.balance_root,key)
|
|
||||||
|
|
||||||
def get_contract_state(self,address,index):
|
# Making updates to the object obtained from this method will do nothing. You need
|
||||||
# Use out internal representation for the key
|
# to call update_contract to finalize the changes.
|
||||||
key = ''.join([chr(hexalpha.find(x)) for x in (address+index).encode('hex')])
|
def get_contract(self,address):
|
||||||
return self.get_state_value(self.contract_root,key)
|
state = rlp.decode(self.state.get(address))
|
||||||
|
if not state or decode(state[0],256) == 0: return False
|
||||||
|
return Trie('statedb',state[2])
|
||||||
|
|
||||||
def get_state_size(self,node):
|
def update_contract(self,address,contract):
|
||||||
if node is None: return 0
|
state = rlp.decode(self.state.get(address))
|
||||||
curnode = self.datastore.get(node,None)
|
if not state or decode(state[0],256) == 0: return False
|
||||||
if not curnode: return 0
|
state[2] = contract.root
|
||||||
elif len(curnode) == 2:
|
self.state.update(address,state)
|
||||||
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):
|
# Serialization method; should act as perfect inverse function of the constructor
|
||||||
# Use out internal representation for the key
|
# assuming no verification failures
|
||||||
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):
|
def serialize(self):
|
||||||
nodes = {}
|
txlist = [x.serialize() for x in self.transactions]
|
||||||
def process(node):
|
header = [ encode(self.number,256),
|
||||||
if node is None: return
|
self.prevhash,
|
||||||
curnode = self.datastore.get(node,None)
|
bin_sha256(rlp.encode(self.siblings)),
|
||||||
if curnode:
|
self.coinbase,
|
||||||
index = sha256(rlp.encode(curnode))
|
self.state.root,
|
||||||
nodes[index] = curnode
|
bin_sha256(rlp.encode(self.txlist)),
|
||||||
if len(node) == 2:
|
encode(self.difficulty,256),
|
||||||
process(curnode[1])
|
encode(self.timestamp,256),
|
||||||
elif len(node) == 16:
|
encode(self.nonce,256),
|
||||||
for i in range(16): process(curnode[i])
|
self.extra ]
|
||||||
process(self.balance_root)
|
return rlp.encode([header, txlist, self.siblings ])
|
||||||
process(self.contract_root)
|
|
||||||
tree_nodes = [nodes[x] for x in nodes]
|
def hash(self):
|
||||||
nodelist = rlp.encode(tree_nodes)
|
return bin_sha256(self.serialize())
|
||||||
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])
|
|
||||||
|
|
195
processblock.py
195
processblock.py
|
@ -1,5 +1,6 @@
|
||||||
from transactions import Transaction
|
from transactions import Transaction
|
||||||
from blocks import Block
|
from blocks import Block
|
||||||
|
import time
|
||||||
|
|
||||||
scriptcode_map = {
|
scriptcode_map = {
|
||||||
0x00: 'STOP',
|
0x00: 'STOP',
|
||||||
|
@ -22,11 +23,11 @@ scriptcode_map = {
|
||||||
0x31: 'RIPEMD-160',
|
0x31: 'RIPEMD-160',
|
||||||
0x32: 'ECMUL',
|
0x32: 'ECMUL',
|
||||||
0x33: 'ECADD',
|
0x33: 'ECADD',
|
||||||
0x34: 'SIGN',
|
0x34: 'ECSIGN',
|
||||||
0x35: 'RECOVER',
|
0x35: 'ECRECOVER',
|
||||||
0x40: 'COPY',
|
0x40: 'COPY',
|
||||||
0x41: 'STORE',
|
0x41: 'STORE',
|
||||||
0x42: 'LD',
|
0x42: 'LOAD',
|
||||||
0x43: 'SET',
|
0x43: 'SET',
|
||||||
0x50: 'JMP',
|
0x50: 'JMP',
|
||||||
0x51: 'JMPI',
|
0x51: 'JMPI',
|
||||||
|
@ -37,73 +38,125 @@ scriptcode_map = {
|
||||||
0x71: 'RAWTX',
|
0x71: 'RAWTX',
|
||||||
0x80: 'DATA',
|
0x80: 'DATA',
|
||||||
0x81: 'DATAN',
|
0x81: 'DATAN',
|
||||||
0x90: 'MYADDRESS'
|
0x90: 'MYADDRESS',
|
||||||
|
0x91: 'BLKHASH',
|
||||||
|
0xff: 'SUICIDE'
|
||||||
}
|
}
|
||||||
|
|
||||||
fees = {
|
params = {
|
||||||
'stepfee': 2**60 * 8192,
|
'stepfee': 2**60 * 4096,
|
||||||
'txfee': 2**60 * 524288,
|
'txfee': 2**60 * 524288,
|
||||||
'memoryfee': 2**60 * 262144
|
'memoryfee': 2**60 * 262144,
|
||||||
|
'datafee': 2**60 * 16384,
|
||||||
|
'cryptofee': 2**60 * 65536,
|
||||||
|
'extrofee': 2**60 * 65536,
|
||||||
|
'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
|
||||||
}
|
}
|
||||||
|
|
||||||
def eval_tx(block):
|
def eval(block,transactions,timestamp,coinbase):
|
||||||
tx = block.transactions.pop(0)
|
h = block.hash()
|
||||||
oldbalance = block.get_balance(tx.from)
|
# Process all transactions
|
||||||
debit = tx.value + tx.fee
|
while len(transactions) > 0:
|
||||||
if tx.to == '':
|
tx = transactions.pop(0)
|
||||||
debit += fees['memoryfee'] * len(filter(lambda x:x > 0,tx.data))
|
fee = params['txfee'] + len(tx.data) * params['datafee']
|
||||||
if oldbalance < debit:
|
if tx.to = '\x00'*20:
|
||||||
return
|
fee += len(tx.data) * params['memoryfee']
|
||||||
block.update_balance(tx.from,oldbalance - debit)
|
# Insufficient fee, do nothing
|
||||||
if tx.to == '':
|
if fee > tx.fee: continue
|
||||||
mk_contract(block,tx) #todo: continue here
|
# 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
|
||||||
|
miner_state = rlp_decode(self.state.get(self.coinbase)) or ['','','']
|
||||||
|
miner_balance = decode(miner_state[1],256)
|
||||||
|
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']
|
||||||
else:
|
else:
|
||||||
block.update_balance(tx.to,block.get_balance(tx.to) + tx.value)
|
reward = params['period_4_reward']
|
||||||
if block.get_contract(tx.to) != 0:
|
miner_balance += reward
|
||||||
eval_contract(block,tx)
|
for sibling in block.siblings:
|
||||||
|
sib_miner_state = rlp_decode(self.state.get(sibling[3]))
|
||||||
|
sib_miner_state[1] = encode(decode(sib_miner_state[1],256)+reward*7/8,256)
|
||||||
|
self.state.update(sibling[3],sib_miner_state)
|
||||||
|
miner_balance += reward/8
|
||||||
|
miner_state[1] = encode(miner_balance,256)
|
||||||
|
self.state.update(self.coinbase,miner_state)
|
||||||
|
# Check timestamp
|
||||||
|
if timestamp < block.timestamp or timestamp > int(time.time()) + 3600:
|
||||||
|
raise new 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 = []
|
||||||
|
block.siblings = []
|
||||||
|
return block
|
||||||
|
|
||||||
def mk_contract(block,tx):
|
def eval_contract(block,transaction_list,tx):
|
||||||
cdata = tx.data
|
|
||||||
# todo: continue here
|
|
||||||
|
|
||||||
|
|
||||||
def eval_contract(block,tx):
|
|
||||||
address = tx.to
|
address = tx.to
|
||||||
# Initialize registers
|
# Initialize registers
|
||||||
reg = [0] * 256
|
reg = [0] * 256
|
||||||
reg[0] = decode(tx.from,16)
|
reg[0] = decode(tx.from,256)
|
||||||
reg[1] = decode(tx.to,16)
|
reg[1] = decode(tx.to,256)
|
||||||
reg[2] = tx.value
|
reg[2] = tx.value
|
||||||
reg[3] = tx.fee
|
reg[3] = tx.fee
|
||||||
index = 0
|
index = 0
|
||||||
stepcounter = 0
|
stepcounter = 0
|
||||||
def monop(code,f):
|
contract = block.get_contract(address)
|
||||||
reg[code[2]] = f(reg[code[1]])
|
if not contract:
|
||||||
def binop(code,f):
|
return
|
||||||
reg[code[3]] = f(reg[code[1]],reg[code[2]])
|
|
||||||
while 1:
|
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) ]
|
||||||
|
# Invalid code instruction or STOP code stops execution sans fee
|
||||||
|
if val_at_index >= 256**6 or code[0] == 'STOP':
|
||||||
|
break
|
||||||
# Calculate fee
|
# Calculate fee
|
||||||
totalfee = 0
|
minerfee = 0
|
||||||
|
nullfee = 0
|
||||||
stepcounter += 1
|
stepcounter += 1
|
||||||
if stepcounter > 16:
|
if stepcounter > 16:
|
||||||
totalfee += fees.get("stepfee")
|
minerfee += params["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]]
|
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"]
|
||||||
if c == 'STORE':
|
if c == 'STORE':
|
||||||
existing = block.get_contract_state(address,code[2])
|
existing = block.get_contract_state(address,code[2])
|
||||||
if reg[code[1]] != 0: fee += fees["MEMORYFEE"]
|
if reg[code[1]] != 0: nullfee += params["memoryfee"]
|
||||||
if existing: fee -= fees["MEMORYFEE"]
|
if existing: nullfee -= params["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':
|
# If we can't pay the fee, break, otherwise pay it
|
||||||
|
if block.get_balance(address) < minerfee + nullfee:
|
||||||
break
|
break
|
||||||
elif c == 'ADD':
|
block.pay_fee(address,nullfee,False)
|
||||||
|
block.pay_fee(address,minerfee,True)
|
||||||
|
|
||||||
|
# Evaluate operations
|
||||||
|
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
|
||||||
elif c == 'MUL':
|
elif c == 'MUL':
|
||||||
reg[code[3]] = (reg[code[1]] * reg[code[2]]) % 2**256
|
reg[code[3]] = (reg[code[1]] * reg[code[2]]) % 2**256
|
||||||
|
@ -166,25 +219,28 @@ def eval_contract(block,tx):
|
||||||
elif code == 'ECADD':
|
elif code == 'ECADD':
|
||||||
pt1 = (reg[code[1]],reg[code[2]])
|
pt1 = (reg[code[1]],reg[code[2]])
|
||||||
pt2 = (reg[code[3]],reg[code[4]])
|
pt2 = (reg[code[3]],reg[code[4]])
|
||||||
|
# Invalid point 1
|
||||||
if (pt1[0] ** 3 + 7 - pt1[1] ** 2) % N != 0:
|
if (pt1[0] ** 3 + 7 - pt1[1] ** 2) % N != 0:
|
||||||
reg[code[5]], reg[code[6]] = 0,0
|
reg[code[5]], reg[code[6]] = 0,0
|
||||||
|
# Invalid point 2
|
||||||
elif (pt2[0] ** 3 + 7 - pt2[1] ** 2) % N != 0:
|
elif (pt2[0] ** 3 + 7 - pt2[1] ** 2) % N != 0:
|
||||||
reg[code[5]], reg[code[6]] = 0,0
|
reg[code[5]], reg[code[6]] = 0,0
|
||||||
|
# Legitimate points
|
||||||
else:
|
else:
|
||||||
pt3 = base10_add(pt1,pt2)
|
pt3 = base10_add(pt1,pt2)
|
||||||
reg[code[5]], reg[code[6]] = pt3[0], pt3[1]
|
reg[code[5]], reg[code[6]] = pt3[0], pt3[1]
|
||||||
elif code == 'SIGN':
|
elif code == 'ECSIGN':
|
||||||
reg[code[3]], reg[code[4]], reg[code[5]] = ecdsa_raw_sign(reg[code[1]],reg[code[2]])
|
reg[code[3]], reg[code[4]], reg[code[5]] = ecdsa_raw_sign(reg[code[1]],reg[code[2]])
|
||||||
elif code == 'RECOVER':
|
elif code == 'ECRECOVER':
|
||||||
pt = ecdsa_raw_recover((reg[code[2]],reg[code[3]],reg[code[4]]),reg[code[1]])
|
pt = ecdsa_raw_recover((reg[code[2]],reg[code[3]],reg[code[4]]),reg[code[1]])
|
||||||
reg[code[5]] = pt[0]
|
reg[code[5]] = pt[0]
|
||||||
reg[code[6]] = pt[1]
|
reg[code[6]] = pt[1]
|
||||||
elif code == 'COPY':
|
elif code == 'COPY':
|
||||||
reg[code[2]] = reg[code[1]]
|
reg[code[2]] = reg[code[1]]
|
||||||
elif code == 'STORE':
|
elif code == 'STORE':
|
||||||
block.update_contract_state(address,encode(reg[code[2]],256,32),reg[code[1]])
|
contract.update(encode(reg[code[2]],256,32),reg[code[1]])
|
||||||
elif code == 'LD':
|
elif code == 'LOAD':
|
||||||
reg[code[2]] = block.get_contract_state(address,encode(reg[code[1]],256,32))
|
reg[code[2]] = contract.get(encode(reg[code[1]],256,32))
|
||||||
elif code == 'SET':
|
elif code == 'SET':
|
||||||
reg[code[1]] = (code[2] + 256 * code[3] + 65536 * code[4] + 16777216 * code[5]) * 2**code[6] % 2**256
|
reg[code[1]] = (code[2] + 256 * code[3] + 65536 * code[4] + 16777216 * code[5]) * 2**code[6] % 2**256
|
||||||
elif code == 'JMP':
|
elif code == 'JMP':
|
||||||
|
@ -194,12 +250,18 @@ def eval_contract(block,tx):
|
||||||
elif code == 'IND':
|
elif code == 'IND':
|
||||||
reg[code[1]] = index
|
reg[code[1]] = index
|
||||||
elif code == 'EXTRO':
|
elif code == 'EXTRO':
|
||||||
address = encode(reg[code[1]] % 2**160,256,20)
|
if reg[code[1]] >= 2**160:
|
||||||
field = encode(reg[code[2]]
|
reg[code[3]] = 0
|
||||||
reg[code[3]] = block.get_contract_state(address,field)
|
else:
|
||||||
|
address = encode(reg[code[1]],256,20)
|
||||||
|
field = encode(reg[code[2]])
|
||||||
|
reg[code[3]] = block.get_contract(address).get(field)
|
||||||
elif code == 'BALANCE':
|
elif code == 'BALANCE':
|
||||||
address = encode(reg[code[1]] % 2**160,256,20)
|
if reg[code[1]] >= 2**160:
|
||||||
reg[code[2]] = block.get_balance(address)
|
reg[code[2]] = 0
|
||||||
|
else:
|
||||||
|
address = encode(reg[code[1]],256,20)
|
||||||
|
reg[code[2]] = block.get_balance(address)
|
||||||
elif code == 'MKTX':
|
elif code == 'MKTX':
|
||||||
to = encode(reg[code[1]],256,32)
|
to = encode(reg[code[1]],256,32)
|
||||||
value = reg[code[2]]
|
value = reg[code[2]]
|
||||||
|
@ -211,20 +273,23 @@ def eval_contract(block,tx):
|
||||||
data = []
|
data = []
|
||||||
for i in range(datan):
|
for i in range(datan):
|
||||||
ind = encode((reg[code[5]] + i) % 2**256,256,32)
|
ind = encode((reg[code[5]] + i) % 2**256,256,32)
|
||||||
data.append(block.get_contract_state(address,ind))
|
data.append(contract.get(ind))
|
||||||
tx = Transaction(to,value,fee,data)
|
tx = Transaction(0,to,value,fee,data)
|
||||||
tx.from = address
|
tx.from = address
|
||||||
block.transactions.append(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]]]
|
||||||
elif code == 'DATAN':
|
elif code == 'DATAN':
|
||||||
reg[code[1]] = len(tx.data)
|
reg[code[1]] = len(tx.data)
|
||||||
elif code == 'MYADDRESS':
|
elif code == 'MYADDRESS':
|
||||||
reg[code[1]] = address
|
reg[code[1]] = address
|
||||||
|
elif code == 'BLKHASH':
|
||||||
|
reg[code[1]] = decode(block.hash())
|
||||||
elif code == 'SUICIDE':
|
elif code == 'SUICIDE':
|
||||||
sz = block.get_contract_size(address)
|
sz = contract.get_size()
|
||||||
negfee = sz * fees["memoryfee"]
|
negfee = -sz * params["memoryfee"]
|
||||||
toaddress = encode(reg[code[1]],256,32)
|
toaddress = encode(reg[code[1]],256,20)
|
||||||
block.update_balance(toaddress,block.get_balance(toaddress) + negfee)
|
block.pay_fee(roaddress,negfee,False)
|
||||||
block.update_contract(address,0)
|
contract.root = ''
|
||||||
break
|
break
|
||||||
|
block.update_contract(address,contract)
|
||||||
|
|
1
rlp.py
1
rlp.py
|
@ -25,6 +25,7 @@ def num_to_var_int(n):
|
||||||
return ''.join([chr(x) for x in s])
|
return ''.join([chr(x) for x in s])
|
||||||
|
|
||||||
def __decode(s):
|
def __decode(s):
|
||||||
|
if s == '': return None
|
||||||
o = []
|
o = []
|
||||||
index = [0]
|
index = [0]
|
||||||
def read_var_int():
|
def read_var_int():
|
||||||
|
|
|
@ -7,32 +7,37 @@ class Transaction():
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
self.parse(args[1])
|
self.parse(args[1])
|
||||||
else:
|
else:
|
||||||
self.to = args[1]
|
self.nonce = args[1]
|
||||||
self.value = args[2]
|
self.to = args[2]
|
||||||
self.fee = args[3]
|
self.value = args[3]
|
||||||
self.data = args[4]
|
self.fee = args[4]
|
||||||
if len(args) > 5: self.sig = args[5]
|
self.data = args[5]
|
||||||
|
|
||||||
def lpad(inp,L): return '\x00' * max(0,L - len(inp)) + inp
|
|
||||||
|
|
||||||
def parse(self,data):
|
def parse(self,data):
|
||||||
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.to = lpad(o[0],20)
|
self.nonce = decode(o[0],256)
|
||||||
self.value = decode(o[1],256)
|
self.to = o[1]
|
||||||
self.fee = decode(o[2],256)
|
self.value = decode(o[2],256)
|
||||||
self.data = rlp.unparse(o[-3])
|
self.fee = decode(o[3],256)
|
||||||
self.sig = o[-4]
|
self.data = rlp.unparse(o[4])
|
||||||
rawhash = sha256(rlp.encode([self.to,self.value,self.fee,self.data]))
|
self.v = o[5]
|
||||||
v,r,s = ord(self.sig[0]), decode(self.sig[1:33],256), decode(self.sig[33:],256)
|
self.r = o[6]
|
||||||
self.from = hash160(ecdsa_raw_recover(rawhash,(v,r,s)))
|
self.s = o[7]
|
||||||
|
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)))
|
||||||
|
|
||||||
def sign(self,key):
|
def sign(self,key):
|
||||||
rawhash = sha256(rlp.parse([self.to,self.value,self.fee,self.data]))
|
rawhash = sha256(rlp.parse([self.to,self.value,self.fee,self.data]))
|
||||||
v,r,s = ecdsa_raw_sign(rawhash,args[5])
|
self.v,self.r,self.s = ecdsa_raw_sign(rawhash,key)
|
||||||
self.sig = chr(v)+encode(r,256,32)+encode(s,256,32)
|
self.from = hash160(privtopub(key))
|
||||||
self.from = hash160(privtopub(args[5]))
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return rlp.parse([self.to, self.value, self.fee, self.data, self.sig]).encode('hex')
|
return rlp.parse([self.nonce, self.to, self.value, self.fee, self.data, self.v, self.r, self.s])
|
||||||
|
|
||||||
|
def hex_serialize(self):
|
||||||
|
return self.serialize().encode('hex')
|
||||||
|
|
||||||
def hash(self):
|
def hash(self):
|
||||||
return bin_sha256(self.serialize())
|
return bin_sha256(self.serialize())
|
||||||
|
|
26
trie.py
26
trie.py
|
@ -12,11 +12,15 @@ class DB():
|
||||||
def put(self,key,value): return self.db.Put(key,value)
|
def put(self,key,value): return self.db.Put(key,value)
|
||||||
def delete(self,key): return self.db.Delete(key)
|
def delete(self,key): return self.db.Delete(key)
|
||||||
|
|
||||||
|
databases = {}
|
||||||
|
|
||||||
class Trie():
|
class Trie():
|
||||||
def __init__(self,db,root='',debug=False):
|
def __init__(self,dbfile,root='',debug=False):
|
||||||
self.root = root
|
self.root = root
|
||||||
self.db = DB(db)
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
if dbfile not in databases:
|
||||||
|
databases[dbfile] = DB(dbfile)
|
||||||
|
self.db = databases[dbfile]
|
||||||
|
|
||||||
def __encode_key(self,key):
|
def __encode_key(self,key):
|
||||||
term = 1 if key[-1] == 16 else 0
|
term = 1 if key[-1] == 16 else 0
|
||||||
|
@ -144,10 +148,28 @@ class Trie():
|
||||||
newnode2 = newnode
|
newnode2 = newnode
|
||||||
return self.__put(newnode2)
|
return self.__put(newnode2)
|
||||||
|
|
||||||
|
def __get_size(self,node):
|
||||||
|
if not node: return 0
|
||||||
|
curnode = self.db.get(node)
|
||||||
|
if not curnode:
|
||||||
|
raise Exception("node not found in database")
|
||||||
|
if len(curnode) == 2:
|
||||||
|
key = self.__decode_key(curnode[0])
|
||||||
|
if key[-1] == 16: return 1
|
||||||
|
else: return self.__get_size(curnode[1])
|
||||||
|
elif len(curnode) == 17:
|
||||||
|
total = 0
|
||||||
|
for i in range(16):
|
||||||
|
total += self.__get_size(curnode[i])
|
||||||
|
if curnode[16]: total += 1
|
||||||
|
return total
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
def get_size(self): return self.__get_size(self.root)
|
||||||
|
|
||||||
def update(self,key,value):
|
def update(self,key,value):
|
||||||
if not isinstance(key,str) or not isinstance(value,str):
|
if not isinstance(key,str) or not isinstance(value,str):
|
||||||
raise Exception("Key and value must be strings")
|
raise Exception("Key and value must be strings")
|
||||||
|
|
Loading…
Reference in New Issue