Skip to content

Commit

Permalink
Merge pull request #6 from musalbas/cross-contract-clean
Browse files Browse the repository at this point in the history
Cross contract clean
  • Loading branch information
musalbas authored Jul 7, 2017
2 parents 2717319 + 3a615f1 commit ca9215f
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 36 deletions.
45 changes: 42 additions & 3 deletions chainspacecontract/chainspacecontract/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,32 @@ def __init__(self, contract_name):

self.methods = {}
self.checkers = {}
self.callbacks = []
self.dependencies = []
self.dependent_transactions_log = []

def __getattr__(self, key):
return self.methods[key]

def _populate_empty_checkers(self):
for method_name, function in self.methods.iteritems():
if method_name not in self.checkers:
self.register_standard_checker(method_name, function)

def _trigger_callbacks(self, transaction):
for callback_function in self.callbacks:
callback_function(transaction)

def register_callback(self, callback_function):
self.callbacks.append(callback_function)

def local_callback(self, transaction):
self.dependent_transactions_log.append(transaction)

def register_dependency(self, contract):
self.dependencies.append(contract)
contract.register_callback(self.local_callback)

def run(self):
self.run_checker_service()

Expand All @@ -32,12 +52,19 @@ def function_wrapper(*args, **kwargs):

@self.flask_app.route('/' + method_name, methods=['POST'], endpoint=method_name)
def checker_request():
dependencies = (request.json['dependencies'] if 'dependencies' in request.json else [])
for dependency in dependencies:
dependency['inputs'] = tuple(dependency['inputs'])
dependency['reference_inputs'] = tuple(dependency['reference_inputs'])
dependency['outputs'] = tuple(dependency['outputs'])

return function_wrapper(
tuple(request.json['inputs']),
tuple(request.json['reference_inputs']),
request.json['parameters'],
tuple(request.json['outputs']),
request.json['returns']
request.json['returns'],
dependencies
)

return function_wrapper
Expand All @@ -54,6 +81,7 @@ def function_wrapper(inputs, reference_inputs, parameters, *args, **kwargs):
if parameters is None:
parameters = {}

self.dependent_transactions_log = []
result = function(inputs, reference_inputs, parameters, *args, **kwargs)

for key in ('outputs', 'returns', 'extra_parameters'):
Expand All @@ -69,6 +97,9 @@ def function_wrapper(inputs, reference_inputs, parameters, *args, **kwargs):

result['contract_id'] = 0

result['dependencies'] = self.dependent_transactions_log

self._trigger_callbacks(result)
return result

self.methods[method_name] = function_wrapper
Expand All @@ -80,6 +111,14 @@ def function_wrapper(inputs, reference_inputs, parameters, *args, **kwargs):

def register_standard_checker(self, method_name, function):
@self.checker(method_name)
def checker(inputs, reference_inputs, parameters, outputs, returns):
def checker(inputs, reference_inputs, parameters, outputs, returns, dependencies):
result = function(inputs, reference_inputs, parameters)
return result['outputs'] == outputs and result['returns'] == returns

for dependency in result['dependencies']:
del dependency['dependencies']

return (
result['outputs'] == outputs
and result['returns'] == returns
and result['dependencies'] == dependencies
)
26 changes: 26 additions & 0 deletions chainspacecontract/chainspacecontract/examples/increment_thrice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""An example smart contract to demonstrate three layer deep cross-contract
calls."""

from chainspacecontract import ChainspaceContract
from chainspacecontract.examples.increment_twice import contract as increment_twice_contract

contract = ChainspaceContract('increment_thrice')
contract.register_dependency(increment_twice_contract)


@contract.method('init')
def init():
return {
'outputs': (0,)
}


@contract.method('increment')
def increment(inputs, reference_inputs, parameters):
increment_twice_contract.increment((reference_inputs[0],), (reference_inputs[1],), None)
return {
'outputs': (inputs[0] + 1,)
}

if __name__ == '__main__':
contract.run()
25 changes: 25 additions & 0 deletions chainspacecontract/chainspacecontract/examples/increment_twice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""An example smart contract to demonstrate cross-contract calls."""

from chainspacecontract import ChainspaceContract
from chainspacecontract.examples.increment import contract as increment_contract

contract = ChainspaceContract('increment_twice')
contract.register_dependency(increment_contract)


@contract.method('init')
def init():
return {
'outputs': (0,)
}


@contract.method('increment')
def increment(inputs, reference_inputs, parameters):
increment_contract.increment((reference_inputs[0],), None, None)
return {
'outputs': (inputs[0] + 1,)
}

if __name__ == '__main__':
contract.run()
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def increment(inputs, reference_inputs, parameters):


@contract.checker('increment')
def increment_checker(inputs, reference_inputs, parameters, outputs, returns):
def increment_checker(inputs, reference_inputs, parameters, outputs, returns, dependencies):
result = increment(inputs, reference_inputs, parameters)
return result['outputs'] == outputs and result['returns'] == returns

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@

import requests

from chainspacecontract.contract import ChainspaceContract
from chainspacecontract.examples.increment import contract as increment_contract
from chainspacecontract.examples.increment_with_custom_checker import contract as increment_with_custom_checker_contract
from chainspacecontract.examples.bank_unauthenticated import contract as bank_unauthenticated_contract
from chainspacecontract.examples.bank_authenticated import contract as bank_authenticated_contract
from chainspacecontract.examples import bank_authenticated
from chainspacecontract.examples.increment_twice import contract as increment_twice_contract
from chainspacecontract.examples.increment_thrice import contract as increment_thrice_contract


class TestIncrement(unittest.TestCase):
"""
class TestExamples(unittest.TestCase):
def test_increment_checker_service(self):
checker_service_process = Process(target=increment_contract.run_checker_service)
checker_service_process.start()
Expand Down Expand Up @@ -97,46 +95,98 @@ def test_bank_unauthenticated_checker_service(self):

checker_service_process.terminate()
checker_service_process.join()
"""



#############################################################################################
# test an authenticated bank transfer
#############################################################################################
def test_bank_authenticated_checker_service(self):
checker_service_process = Process(target=bank_authenticated_contract.run_checker_service)
def test_increment_twice(self):
checker_service_process = Process(target=increment_twice_contract.run_checker_service)
checker_service_process.start()
time.sleep(0.1)

# NOTE: export public key
"""
G = EcGroup()
g = G.generator()
priv = G.order().random()
pub = priv * g
byte_string = pub.export()
print hexlify(byte_string)
print EcPt.from_binary(byte_string, G) == pub
"""

response = requests.post('http://127.0.0.1:5000/auth_transfer',
json=bank_authenticated.auth_transfer(
[{'name': 'alice', 'balance': 10}, {'name': 'bob', 'balance': 10}],
None,
{'amount': 3},
'83C72CF7E1BA9F120C5A45135A0FE3DA59D7771BB9C670B63134A8B0'
)
)
response = requests.post('http://127.0.0.1:5000/increment', json={
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'dependencies': [{
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'reference_inputs': ()
}],
'reference_inputs': (0, )
})
response_json = response.json()
self.assertTrue(response_json['success'])

response = requests.post('http://127.0.0.1:5000/increment', json={
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'dependencies': [{
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (0, ),
'returns': {},
'reference_inputs': ()
}],
'reference_inputs': (0, )
})
response_json = response.json()
self.assertFalse(response_json['success'])

checker_service_process.terminate()
checker_service_process.join()

def test_increment_thrice(self):
checker_service_process = Process(target=increment_thrice_contract.run_checker_service)
checker_service_process.start()
time.sleep(0.1)

response = requests.post('http://127.0.0.1:5000/increment', json={
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'dependencies': [{
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'reference_inputs': (0, )
}],
'reference_inputs': (0, 0)
})
response_json = response.json()
self.assertTrue(response_json['success'])

response = requests.post('http://127.0.0.1:5000/increment', json={
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (0, ),
'returns': {},
'dependencies': [{
'inputs': (0, ),
'contract_id': 0,
'parameters': {},
'outputs': (1, ),
'returns': {},
'reference_inputs': (0, )
}],
'reference_inputs': (0, 0)
})
response_json = response.json()
self.assertFalse(response_json['success'])

checker_service_process.terminate()
checker_service_process.join()

if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from multiprocessing import Process
import time
import unittest

import requests

from chainspacecontract.examples.bank_authenticated import contract as bank_authenticated_contract
from chainspacecontract.examples import bank_authenticated


class TestBankAuthenticated(unittest.TestCase):
#############################################################################################
# test an authenticated bank transfer
#############################################################################################
def test_bank_authenticated_checker_service(self):
checker_service_process = Process(target=bank_authenticated_contract.run_checker_service)
checker_service_process.start()
time.sleep(0.1)

# NOTE: export public key
"""
G = EcGroup()
g = G.generator()
priv = G.order().random()
pub = priv * g
byte_string = pub.export()
print hexlify(byte_string)
print EcPt.from_binary(byte_string, G) == pub
"""

response = requests.post('http://127.0.0.1:5000/auth_transfer',
json=bank_authenticated.auth_transfer(
[{'name': 'alice', 'balance': 10}, {'name': 'bob', 'balance': 10}],
None,
{'amount': 3},
'83C72CF7E1BA9F120C5A45135A0FE3DA59D7771BB9C670B63134A8B0'
)
)
response_json = response.json()
self.assertTrue(response_json['success'])

checker_service_process.terminate()
checker_service_process.join()

if __name__ == '__main__':
unittest.main()

0 comments on commit ca9215f

Please sign in to comment.