# Vitalik's first pyethereum commit from 2013-12-19 Source: https://github.com/ethereum/pyethereum/commit/6654544ca7245fa0f501057a94e74c8e331af2b0 Kiwi News: https://news.kiwistand.com/stories/Vitaliks-first-pyethereum-commit-from-2013-12-19?index=0x6582d170b793bf84b1541c0eb375e5ac47b45c2d3806ead0908c08899a96ee9c8937f2ee Submitted by tim on 2023-12-20 UTC, 1 upvote, 0 comments ## Summary pyethereum's Block class, introduced in a commit dated 2013-12-19 by Vitalik, stores blockchain state in a Merkle trie keyed by hexadecimal-encoded addresses. It decodes a block from RLP data, checking the balance root, contract root, transaction list hash and sibling list against their stated hashes. Methods update and read balances and contract storage, compute state and contract size, and serialize the block back into RLP. The code contains bugs, such as a sibling-root check that refers to an undefined variable and a get_state_value method that uses curnode before assignment. ## Article 1+ from pybitcointools import * 2+ import rlp 3+ import re 4+ from transactions import Transaction 5+6+ class Block(): 7+ def __init__(self,data=None): 8+ if not data: 9+ return 10+ if re.match('^[0-9a-fA-F]*$',data): 11+ data = data.decode('hex') 12+ header, tree_node_list, transaction_list, sibling_list = rlp.decode(data) 13+ h = rlp.decode(header) 14+ self.prevhash = encode(h[0],16,64) 15+ self.coinbase = encode(h[1],16,40) 16+ self.balance_root = encode(h[2],256,32) 17+ self.contract_root = encode(h[3],256,32) 18+ self.difficulty = h[4] 19+ self.timestamp = h[5] 20+ transactions_root = encode(h[6],256,32) 21+ siblings_root = encode(h[7],256,32) 22+ self.nonce = h[8] 23+ self.datastore = {} 24+ for nd in rlp.decode(tree_node_list): 25+ ndk = bin_sha256(nd) 26+ self.datastore[ndk] = rlp.decode(nd) 27+ self.transactions = [Transaction(x) for x in rlp.decode(transaction_list)] 28+ self.siblings = [rlp.decode(x) for x in rlp.decode(sibling_list)] 29+ # Verifications 30+ if self.balance_root != '' and self.balance_root not in self.datastore: 31+ raise Exception("Balance Merkle root not found!") 32+ if self.contract_root != '' and self.contract_root not in self.datastore: 33+ raise Exception("Contract Merkle root not found!") 34+ if bin_sha256(transaction_list) != transactions_root: 35+ raise Exception("Transaction list root hash does not match!") 36+ if bin_sha256(sibling_list) != sibling_root: 37+ raise Exception("Transaction list root hash does not match!") 38+ for sibling in self.siblings: 39+ if sibling[0] != self.prevhash: 40+ raise Exception("Sibling's parent is not my parent!") 41+42+43+ hexalpha = '0123456789abcdef' 44+45+ def get_updated_state(self,node,key,value): 46+ curnode = self.datastore.get(node,None) 47+ # Insertion case 48+ if value != 0 and value != '': 49+ # Base case 50+ if key == '': 51+ return value 52+ # Inserting into an empty trie 53+ if not curnode: 54+ newnode = [ key, value ] 55+ k = sha256(rlp.encode(newnode)) 56+ self.datastore[k] = newnode 57+ return k 58+ elif len(curnode) == 2: 59+ # Inserting into a (k,v), same key 60+ if key == curnode[0]: 61+ newnode = [ key, value ] 62+ k = sha256(rlp.encode(newnode)) 63+ self.datastore[k] = newnode 64+ return k 65+ # Inserting into a (k,v), different key 66+ else: 67+ i = 0 68+ while key[:i] == curnode[0][:i]: i += 1 69+ k1 = self.get_updated_state(None,curnode[0][i:],curnode[1]) 70+ k2 = self.get_updated_state(None,key[i:],value) 71+ newnode3 = [ None ] * 16 72+ newnode3[ord(curnode[0][0])] = k1 73+ newnode3[ord(key[0])] = k2 74+ k3 = sha256(rlp.encode(newnode3)) 75+ self.datastore[k3] = newnode3 76+ # No prefix sharing 77+ if i == 1: 78+ return k3 79+ # Prefix sharing 80+ else: 81+ newnode4 = [ key[:i-1], k3 ] 82+ k4 = sha256(rlp.encode(newnode4)) 83+ self.datastore[k4] = newnode4 84+ return k4 85+ else: 86+ # inserting into a 16-array 87+ newnode1 = self.get_updated_state(curnode[ord(key[0])],key[1:],value) 88+ newnode2 = [ curnode[i] for i in range(16) ] 89+ newnode2[ord(key[0])] = newnode1 90+ return newnode2 91+ # Deletion case 92+ else: 93+ # Base case 94+ if key == '': 95+ return None 96+ # Deleting from a (k,v); obvious 97+ if len(curnode) == 2: 98+ if key == curnode[0]: return None 99+ else: return node 100+ else: 101+ k1 = self.get_updated_state(curnode[ord(key[0])],key[1:],value) 102+ newnode = [ curnode[i] for i in range(16) ] 103+ newnode[ord(key[0])] = k1 104+ totalnodes = sum([ 1 if newnode2[i] else 0 for i in range(16) ]) 105+ if totalnodes == 0: 106+ raise Exception("Can't delete from two to zero! Error! Waahmbulance!") 107+ elif totalnodes == 1: 108+ # If only have one node left, we revert to (key, value) 109+ node_index = [i for i in range(16) if newnode2[i]][0] 110+ node2 = self.datastore[curnode[node_index]] 111+ if len(node2) == 2: 112+ # If it's a (key, value), we just prepend to the key 113+ newnode = [ chr(node_index) + node2[0], node2[1] ] 114+ else: 115+ # Otherwise, we just make a single-char (key, value) pair 116+ newnode = [ chr(node_index), curnode[node_index] ] 117+ k2 = sha256(rlp.encode(newnode)) 118+ self.datastore[k2] = newnode 119+ return k2 120+121+122+ def update_balance(self,address,value): 123+ # Use out internal representation for the key 124+ key = ''.join([chr(hexalpha.find(x)) for x in address.encode('hex')]) 125+ self.balance_root = self.get_updated_state(self.balance_root,key,value) 126+127+ def update_contract_state(self,address,index,value): 128+ # Use out internal representation for the key 129+ key = ''.join([chr(hexalpha.find(x)) for x in (address+index).encode('hex')]) 130+ self.contract_root = self.get_updated_state(self.contract_root,key,value) 131+132+ def get_state_value(self,node,key): 133+ if key == '': 134+ return node 135+ if not curnode: 136+ return None 137+ curnode = self.datastore.get(node,None) 138+ return self.get_state_value(curnode[ord(key[0])],key[1:]) 139+140+ def get_balance(self,address): 141+ # Use out internal representation for the key 142+ key = ''.join([chr(hexalpha.find(x)) for x in (address).encode('hex')]) 143+ return self.get_state_value(self.balance_root,key) 144+145+ def get_contract_state(self,address,index): 146+ # Use out internal representation for the key 147+ key = ''.join([chr(hexalpha.find(x)) for x in (address+index).encode('hex')]) 148+ return self.get_state_value(self.contract_root,key) 149+150+ def get_state_size(self,node): 151+ if node is None: return 0 152+ curnode = self.datastore.get(node,None) 153+ if not curnode: return 0 154+ elif len(curnode) == 2: 155+ return self.get_state_size(curnode[1]) 156+ else: 157+ total = 0 158+ for i in range(16): total += self.get_state_size(curnode[i]) 159+ return total 160+161+ def get_contract_size(self,address): 162+ # Use out internal representation for the key 163+ key = ''.join([chr(hexalpha.find(x)) for x in (address).encode('hex')]) 164+ return self.get_state_size(self.get_state_value(self.contract_root,key)) 165+166+ def serialize(self): 167+ nodes = {} 168+ def process(node): 169+ if node is None: return 170+ curnode = self.datastore.get(node,None) 171+ if curnode: 172+ index = sha256(rlp.encode(curnode)) 173+ nodes[index] = curnode 174+ if len(node) == 2: 175+ process(curnode[1]) 176+ elif len(node) == 16: 177+ for i in range(16): process(curnode[i]) 178+ process(self.balance_root) 179+ process(self.contract_root) 180+ tree_nodes = [nodes[x] for x in nodes] 181+ nodelist = rlp.encode(tree_nodes) 182+ txlist = rlp.encode([x.serialize() for x in self.transactions]) 183+ siblinglist = rlp.encode(self.siblings) 184+ header = rlp.encode([self.prevhash, self.coinbase, self.balance_root, self.contract_root, self.difficulty, self.timestamp, bin_sha256(txlist), bin_sha256(siblinglist]) 185+ return rlp.encode([header, nodelist, txlist, siblinglist])