Skip to content

Commit

Permalink
example contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Kelly authored and Robert Kelly committed Jul 16, 2019
1 parent 434bf62 commit 6e2cec3
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 41 deletions.
54 changes: 39 additions & 15 deletions block.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations

import binascii
from hashlib import sha256
import json
import pickle
Expand All @@ -8,18 +11,22 @@

class Block(object):
def __init__(self, creator: str, prev_block_header_hash: bytes=None):
self.creator = creator
self.nonce = 0
self.transactions = [Coinbase(creator)]
self.previous_block_header_hash = prev_block_header_hash
self.time = bytes(int(time.time()))
self.block_header_hash = None
self.merkle_root = None

def add_transaction(self, transaction: Transaction):
self.transactions.append(transaction)
def _reset_header_hashes(self):
self.block_header_hash = None
self.merkle_root = None

def add_transaction(self, transaction: Transaction):
self.transactions.append(transaction)
self._reset_header_hashes()

def increment_nonce(self):
self.nonce += 1

Expand Down Expand Up @@ -54,21 +61,39 @@ def get_merkle_root(self) -> bytes:
self.merkle_root = transaction_strings[0]
return self.merkle_root

def verify(self):
# TODO: Implement block verification
pass

def dumps(self):
return pickle.dumps(self).hex()
def verify_transactions(self):
for transaction in self.transactions:
assert transaction.verify()

@staticmethod
def loads(data):
return pickle.loads(bytes.fromhex(data))
def loads(data) -> Block:
data = json.loads(data)
b = Block(
creator=data['creator'],
prev_block_header_hash= binascii.unhexlify(data['previous_block_header_hash']),
)
b.transactions = [Transaction.loads(binascii.unhexlify(t)) for t in data['transactions']]
b.time = binascii.unhexlify(data['time'])
b.block_header_hash = data['block_header_hash']
b.merkle_root = binascii.unhexlify(data['merkle_root'])
b.nonce = data['nonce']
return b

def dumps(self) -> str:
return json.dumps({
'creator': self.creator,
'nonce': self.nonce,
'transactions': [t.dumps(with_sig=True).hex() for t in self.transactions],
'previous_block_header_hash': self.previous_block_header_hash.hex(),
'time': self.time.hex(),
'block_header_hash': self.block_header_hash.hex(),
'merkle_root': self.merkle_root.hex(),
}).encode('utf-8')


class GenesisBlock(Block):
def __init__(self):
super(GenesisBlock, self).__init__(b'Noone', b'Nothing')
super(GenesisBlock, self).__init__('Noone', b'Nothing')
self.block_header_hash = b'fafa'

def get_merkle_root(self):
Expand All @@ -80,15 +105,14 @@ def test():
t = generate_test_transaction(random=False)
b = Block('3a1bcfa12a29a7ed68b1c70743a104cc37e6ae8e2c94653fcc1093707a62c448f98ac599df92d392a9f56c2a46bca5a375b9996f321c490b2e92c7c71cf1e134', b'')
b.add_transaction(t)
b.get_merkle_root()
b.set_time(b'10000000')
assert b.get_header_hash() == '66133b9317522b44b4a0acce652b8efb561b0e892f7a1b0e4ba603848f3f2ac1'
b.get_merkle_root()
assert b.get_header_hash().hex() == '24d5e60a7877a34527afefbb2e05c7940789f3c75dc783f23f85b81336c0881d'
c = b.dumps()
b = Block.loads(c)
assert b.get_header_hash() == '66133b9317522b44b4a0acce652b8efb561b0e892f7a1b0e4ba603848f3f2ac1'
assert b.get_header_hash().hex() == '24d5e60a7877a34527afefbb2e05c7940789f3c75dc783f23f85b81336c0881d'

gb = GenesisBlock()
print(len(gb.dumps()))
gb.get_merkle_root()
gb.get_header_hash()

Expand Down
11 changes: 6 additions & 5 deletions blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,23 @@ def __init__(self, creator: str, prev_blocks: List[Block]=None):
self.prev_block = self.prev_blocks[-1]

def update(self, block: Block):
self.verify_block(block)
self.prev_blocks.append(block)
self.current_block = Block(creator, self.prev_blocks[-1].block_header_hash)

def verify_transaction(self, transaction: Transaction) -> bool:
return transaction.sender in self.accounts and transaction.amount < self.accounts[transaction.sender]

def verify_block(self) -> bool:
for transaction in self.current_block.transactions:
assert transaction.verify()
assert block.get_merkle_root == block.merkle_root
def verify_block(self, block: Block=None) -> bool:
block = self.current_block if block is None else block
block.verify_transactions()
cur_merkle_root = block.merkle_root
assert block.get_merkle_root() == cur_merkle_root
assert int(block.get_header_hash().hex(), 16) < int(difficulty, 16)
return True

def mine_block(self) -> bool:
self.current_block.get_merkle_root()
print(self.prev_blocks[-1].block_header_hash)
block_hash = self.current_block.get_header_hash().hex()
score = int(block_hash, 16) - int(self.difficulty, 16)
print('score:', score)
Expand Down
36 changes: 32 additions & 4 deletions contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class SmartContract(object):
'PUSH': push,
'POP': pop,
'SWAP': swap,
'DUP': dup,
'NOW': now,
'ADD': add,
'SUB': sub,
'MUL': mul,
Expand Down Expand Up @@ -54,6 +56,7 @@ def delete_memory(contract):
contract.memory = {}

def send_amount(contract, amount: int, receiver: str):
print(f'SENDING {amount} to {receiver}')
if contract.balance < int(amount):
raise Exception('Invalid Balance')
contract.balance -= int(amount)
Expand All @@ -77,6 +80,7 @@ def execute(contract, gas_balance, sent_amount: int, sender: str, args: dict={})
values = row.strip().split(' ')
# control flow for IF
if values[0] == 'IF':
print('USING LEFT OPTION')
top = contract.stack.pop()
if top == '0':
values[3:]
Expand All @@ -88,10 +92,12 @@ def execute(contract, gas_balance, sent_amount: int, sender: str, args: dict={})
contract.delete_memory()


if __name__ == '__main__':
def basic_example():
code = '''
SLOAD magic_word
MLOAD magic_word
PUSH magic_word
SLOAD
PUSH my_guess
MLOAD
EQUALS
SENDER
SWAP
Expand All @@ -100,4 +106,26 @@ def execute(contract, gas_balance, sent_amount: int, sender: str, args: dict={})
IF TRANSFER ELSE UNIT
'''
s = SmartContract(33, code, {'magic_word': 'boo'})
s.execute(100, 10, 'fafafafa', {'magic_word': 'boo'})
s.execute(100, 10, 'fafafafa', {'my_guess': 'boo'})


def basic_example2():
code = '''
SENDER
SLOAD
AMOUNT
PUSH 3
MUL
ADD
SENDER
SSTORE
SENDER
SLOAD
'''
s = SmartContract(33, code, {'magic_word': 'boo', 'fafafafa': '200'})
s.execute(100, 10, 'fafafafa', {'my_guess': 'boo'})


if __name__ == '__main__':
basic_example1()
basic_example2()
39 changes: 27 additions & 12 deletions opcodes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import time


class Stack(object):
def __init__(self, contract):
self.stack = []
Expand All @@ -19,10 +22,19 @@ def push(stack: Stack, *args):
def pop(stack: Stack, *args):
return stack.pop()

def dup(stack: Stack, *args):
val = stack[-1]
stack.push(val)

def now(stack: Stack, *args):
stack.push(str(int(time.time())))

def add(stack: Stack, *args):
x = stack.pop()
y = stack.pop()
stack.push(x + y)
x = 0 if x == 'Unit' else int(x)
y = 0 if y == 'Unit' else int(y)
stack.push(str(x + y))

def sub(stack: Stack, *args):
x = stack.pop()
Expand All @@ -32,7 +44,9 @@ def sub(stack: Stack, *args):
def mul(stack: Stack, *args):
x = stack.pop()
y = stack.pop()
stack.push(x * y)
x = 0 if x == 'Unit' else int(x)
y = 0 if y == 'Unit' else int(y)
stack.push(str(x * y))

def div(stack: Stack, *args):
x = stack.pop()
Expand Down Expand Up @@ -73,30 +87,31 @@ def swap(stack: Stack, *args):
stack.push(y)

def sstore(stack: Stack, *args):
key: str = args[0]
value = args[1]
key: str = stack.pop()
value = stack.pop()
stack.contract.update_storage(key, value)

def sload(stack: Stack, *args):
key: str = args[0]
stack.push(stack.contract.storage[key])
key: str = stack.pop()
try:
stack.push(stack.contract.storage[key])
except KeyError:
stack.push('Unit')

def mstore(stack: Stack, *args):
key: str = args[0]
value = args[1]
key: str = stack.pop()
value = stack.pop()
stack.contract.update_memory(key, value)

def mload(stack: Stack, *args):
print(stack.contract.memory)
key: str = args[0]
key: str = stack.pop()
stack.push(stack.contract.memory[key])

def sender(stack: Stack, *args):
stack.push(stack.contract.sender)

def amount(stack: Stack, *args):
sender = sel
stack.push(stack.contract.amount)
stack.push(str(stack.contract.sent_amount))

def balance(stack: Stack, *args):
stack.push(str(stack.contract.balance))
Expand Down
27 changes: 22 additions & 5 deletions transaction.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations

import binascii
import json

from signatures import generate_keypair, sign_message, verify
Expand All @@ -9,7 +12,7 @@ def __init__(
sender: str,
receiver: str,
amount: int,
signature: str=None,
signature: string='',
):
self.sender = sender
self.receiver = receiver
Expand All @@ -18,7 +21,7 @@ def __init__(

def verify(self) -> bool:
try:
assert self.signature
assert self.signature != ''
assert verify(self.dumps(), self.signature, self.sender)
return True
except:
Expand All @@ -28,10 +31,24 @@ def sign(self, private_key: str) -> str:
self.signature = sign_message(self.dumps(), private_key)
return self.signature

def dumps(self) -> str:
@staticmethod
def loads(data) -> Transaction:
data = json.loads(data)
if 'signature' in data:
data['signature'] = binascii.unhexlify(data['signature'])
return Transaction(**data)

def dumps(self, with_sig: bool=False) -> bytes:
if with_sig:
return json.dumps({
'sender': self.sender,
'receiver': self.receiver,
'amount': self.amount,
'signature': self.signature,
}).encode('utf-8')
return json.dumps({
'from': self.sender,
'to': self.receiver,
'sender': self.sender,
'receiver': self.receiver,
'amount': self.amount,
}).encode('utf-8')

Expand Down

0 comments on commit 6e2cec3

Please sign in to comment.